home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / kernel / vm / vmPage.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-12-19  |  78.1 KB  |  2,976 lines

  1. /* 
  2.  * vmPage.c --
  3.  *
  4.  *      This file contains routines that manage the core map, allocate
  5.  *      list, free list, dirty list and reserve page list.  The core map
  6.  *    contains one entry for each page frame in physical memory.  The four
  7.  *    lists run through the core map.  The dirty list contains all pages
  8.  *    that are being written to disk.  Dirty pages are written out by
  9.  *    a set of pageout processes.  Pages are put onto the dirty list by the
  10.  *    page allocation routine.  The allocate list contains all pages that
  11.  *    are being used by user processes and are not on the dirty list.  It is
  12.  *    kept in approximate LRU order by a version of the clock algorithm. 
  13.  *    The free list contains pages that aren't being used by any user
  14.  *    processes or the kernel.  The reserve list is a few pages
  15.  *    that are set aside for emergencies when the kernel needs memory but 
  16.  *    all of memory is dirty.
  17.  *
  18.  *    LOCKING PAGES
  19.  *
  20.  *    In general all pages that are on the allocate page list are eligible 
  21.  *    to be given to any process.  However, if a page needs to be locked down
  22.  *    so that it cannot be taken away from its owner, there is a lock count
  23.  *    field in the core map entry for a page frame to allow this.  As long
  24.  *    as the lock count is greater than zero, the page cannot be taken away
  25.  *    from its owner.
  26.  *    
  27.  * Copyright (C) 1985 Regents of the University of California
  28.  * All rights reserved.
  29.  */
  30.  
  31. #ifndef lint
  32. static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/vm/vmPage.c,v 9.20 91/09/24 21:32:50 shirriff Exp $ SPRITE (Berkeley)";
  33. #endif not lint
  34.  
  35. #include <sprite.h>
  36. #include <vmStat.h>
  37. #include <vm.h>
  38. #include <vmInt.h>
  39. #include <vmSwapDir.h>
  40. #include <user/vm.h>
  41. #include <sync.h>
  42. #include <dbg.h>
  43. #include <list.h>
  44. #include <timer.h>
  45. #include <lock.h>
  46. #include <sys.h>
  47. #include <fscache.h>
  48. #include <fsio.h>
  49. #include <fsrmt.h>
  50. #include <stdio.h>
  51.  
  52. Boolean    vmDebug    = FALSE;
  53.  
  54. static    VmCore          *coreMap;    /* Pointer to core map that is 
  55.                        allocated in VmCoreMapAlloc. */
  56.  
  57. extern int debugVmStubs; /* Unix compatibility debug flag. */
  58.  
  59. /*
  60.  * Minimum fraction of pages that VM wants for itself.  It keeps
  61.  * 1 / MIN_VM_PAGE_FRACTION of the available pages at boot time for itself.
  62.  */
  63. #define    MIN_VM_PAGE_FRACTION    16
  64.  
  65. /*
  66.  * Variables to define the number of page procs working at a time and the
  67.  * maximum possible number that can be working at a time.
  68.  */
  69. static    int    numPageOutProcs = 0;
  70. int        vmMaxPageOutProcs = VM_MAX_PAGE_OUT_PROCS;
  71.  
  72. /*
  73.  * Page lists.  There are four different lists and a page can be on at most
  74.  * one list.  The allocate list is a list of in use pages that is kept in
  75.  * LRU order. The dirty list is a list of in use pages that are being
  76.  * written to swap space.  The free list is a list of pages that are not
  77.  * being used by any process.  The reserve list is a list with
  78.  * NUM_RESERVE_PAGES on it that is kept for the case when the kernel needs
  79.  * new memory but all of memory is dirty.
  80.  */
  81. #define    NUM_RESERVE_PAGES    3
  82. static    List_Links      allocPageListHdr;
  83. static    List_Links      dirtyPageListHdr;
  84. static    List_Links    freePageListHdr;
  85. static    List_Links    reservePageListHdr;
  86. #define allocPageList    (&allocPageListHdr)
  87. #define dirtyPageList    (&dirtyPageListHdr)
  88. #define    freePageList    (&freePageListHdr)
  89. #define    reservePageList    (&reservePageListHdr)
  90.  
  91. /*
  92.  * Condition to wait for a clean page to be put onto the allocate list.
  93.  */
  94. Sync_Condition    cleanCondition;    
  95.  
  96. /*
  97.  * Variables to allow recovery.
  98.  */
  99. static    Boolean        swapDown = FALSE;
  100. Sync_Condition    swapDownCondition;
  101.  
  102. /*
  103.  * Maximum amount of pages that can be on the dirty list before waiting for
  104.  * a page to be cleaned.  It is a function of the amount of free memory at
  105.  * boot time.
  106.  */
  107. #define    MAX_DIRTY_PAGE_FRACTION    4
  108. int    vmMaxDirtyPages;
  109.  
  110. Boolean    vmFreeWhenClean = TRUE;    
  111. Boolean    vmAlwaysRefuse = FALSE;    
  112. Boolean    vmAlwaysSayYes = FALSE;    
  113. Boolean    vmWriteablePageout = FALSE;
  114. Boolean vmWriteableRefPageout = FALSE;
  115.  
  116. int    vmFSPenalty = 0;
  117. int    vmNumPageGroups = 10;
  118. int    vmPagesPerGroup;
  119. int    vmCurPenalty;
  120. int    vmBoundary;
  121. Boolean    vmCORReadOnly = FALSE;
  122.  
  123. /*
  124.  * Limit to put on the number of pages the machine can have.  Used for
  125.  * benchmarking purposes only.
  126.  */
  127. int    vmPhysPageLimit = -1;
  128.  
  129. static void PageOut _ARGS_((ClientData data, Proc_CallInfo *callInfoPtr));
  130. static void PutOnReserveList _ARGS_((register VmCore *corePtr));
  131. static void PutOnFreeList _ARGS_((register VmCore *corePtr));
  132.  
  133.  
  134. /*
  135.  * ----------------------------------------------------------------------------
  136.  *
  137.  * VmCoreMapAlloc --
  138.  *
  139.  *         Allocate space for the core map.
  140.  *
  141.  * Results:
  142.  *         None.
  143.  *
  144.  * Side effects:
  145.  *    Core map allocated.
  146.  * ----------------------------------------------------------------------------
  147.  */
  148. void
  149. VmCoreMapAlloc()
  150. {
  151.     if (vmPhysPageLimit > 0 && vmPhysPageLimit < vmStat.numPhysPages) {
  152.     vmStat.numPhysPages = vmPhysPageLimit;
  153.     }
  154.     printf("Available memory %d\n", vmStat.numPhysPages * vm_PageSize);
  155.     coreMap = (VmCore *) Vm_BootAlloc(sizeof(VmCore) * vmStat.numPhysPages);
  156.     bzero((char *) coreMap, sizeof(VmCore) * vmStat.numPhysPages);
  157. }
  158.  
  159.  
  160. /*
  161.  * ----------------------------------------------------------------------------
  162.  *
  163.  * VmCoreMapInit --
  164.  *
  165.  *         Initialize the core map.
  166.  *
  167.  * Results:
  168.  *         None.
  169.  *
  170.  * Side effects:
  171.  *    Core map initialized.
  172.  * ----------------------------------------------------------------------------
  173.  */
  174. void
  175. VmCoreMapInit()
  176. {
  177.     register    int    i;
  178.     register    VmCore    *corePtr;
  179.     int            firstKernPage;
  180.  
  181.     /*   
  182.      * Initialize the allocate, dirty, free and reserve lists.
  183.      */
  184.     List_Init(allocPageList);
  185.     List_Init(dirtyPageList);
  186.     List_Init(freePageList);
  187.     List_Init(reservePageList);
  188.  
  189.     firstKernPage = (unsigned int)mach_KernStart >> vmPageShift;
  190.     /*
  191.      * Initialize the core map.  All pages up to vmFirstFreePage are
  192.      * owned by the kernel and the rest are free.
  193.      */
  194.     for (i = 0, corePtr = coreMap; i < vmFirstFreePage; i++, corePtr++) {
  195.     corePtr->links.nextPtr = (List_Links *) NIL;
  196.     corePtr->links.prevPtr = (List_Links *) NIL;
  197.         corePtr->lockCount = 1;
  198.     corePtr->wireCount = 0;
  199.         corePtr->flags = 0;
  200.         corePtr->virtPage.segPtr = vm_SysSegPtr;
  201.         corePtr->virtPage.page = i + firstKernPage;
  202.     corePtr->virtPage.sharedPtr = (Vm_SegProcList *) NIL;
  203.     }
  204.     /*
  205.      * The first NUM_RESERVED_PAGES are put onto the reserve list.
  206.      */
  207.     for (i = vmFirstFreePage, vmStat.numReservePages = 0;
  208.          vmStat.numReservePages < NUM_RESERVE_PAGES;
  209.      i++, corePtr++) {
  210.     corePtr->links.nextPtr = (List_Links *) NIL;
  211.     corePtr->links.prevPtr = (List_Links *) NIL;
  212.     corePtr->virtPage.sharedPtr = (Vm_SegProcList *) NIL;
  213.     PutOnReserveList(corePtr);
  214.     }
  215.     /*
  216.      * The remaining pages are put onto the free list.
  217.      */
  218.     for (vmStat.numFreePages = 0; i < vmStat.numPhysPages; i++, corePtr++) {
  219.     corePtr->links.nextPtr = (List_Links *) NIL;
  220.     corePtr->links.prevPtr = (List_Links *) NIL;
  221.     corePtr->virtPage.sharedPtr = (Vm_SegProcList *) NIL;
  222.     PutOnFreeList(corePtr);
  223.     }
  224. }
  225.  
  226.  
  227. /*
  228.  * ----------------------------------------------------------------------------
  229.  *
  230.  *    Routines to manage the four lists.
  231.  *
  232.  * ----------------------------------------------------------------------------
  233.  */
  234.  
  235. /*
  236.  * ----------------------------------------------------------------------------
  237.  *
  238.  * PutOnAllocListFront --
  239.  *
  240.  *         Put this core map entry onto the front of the allocate list.  
  241.  *
  242.  * Results:
  243.  *         None.
  244.  *
  245.  * Side effects:
  246.  *    Alloc lists modified and core map entry modified.
  247.  * ----------------------------------------------------------------------------
  248.  */
  249. INTERNAL static void
  250. PutOnAllocListFront(corePtr)
  251.     register    VmCore    *corePtr;
  252. {
  253.     VmListInsert((List_Links *) corePtr, LIST_ATFRONT(allocPageList));
  254.     vmStat.numUserPages++;
  255. }
  256.  
  257.  
  258. /*
  259.  * ----------------------------------------------------------------------------
  260.  *
  261.  * PutOnAllocListRear --
  262.  *
  263.  *         Put this core map entry onto the rear of the allocate list
  264.  *
  265.  * Results:
  266.  *         None.
  267.  *
  268.  * Side effects:
  269.  *    Alloc list modified and core map entry modified.
  270.  * ----------------------------------------------------------------------------
  271.  */
  272. INTERNAL static void
  273. PutOnAllocListRear(corePtr)
  274.     VmCore    *corePtr;
  275. {
  276.     VmListInsert((List_Links *) corePtr, LIST_ATREAR(allocPageList));
  277.     vmStat.numUserPages++;
  278. }
  279.  
  280.  
  281. /*
  282.  * ----------------------------------------------------------------------------
  283.  *
  284.  * PutOnAllocList --
  285.  *
  286.  *         Put the given core map entry onto the end of the allocate list.
  287.  *
  288.  * Results:
  289.  *         None.
  290.  *
  291.  * Side effects:
  292.  *         Core map entry put onto end of allocate list.
  293.  *
  294.  * ----------------------------------------------------------------------------
  295.  */
  296.  
  297. ENTRY static void
  298. PutOnAllocList(virtAddrPtr, page)
  299.     Vm_VirtAddr        *virtAddrPtr;    /* The translated virtual address that 
  300.                      * indicates the segment and virtual
  301.                      * page that this physical page is
  302.                      * being allocated for */
  303.     unsigned int    page;
  304. {
  305.     register    VmCore    *corePtr; 
  306.     Time        curTime;
  307.  
  308.     LOCK_MONITOR;
  309.  
  310.     Timer_GetTimeOfDay(&curTime, (int *) NIL, (Boolean *) NIL);
  311.  
  312.     corePtr = &coreMap[page];
  313.  
  314.     /*
  315.      * Move the page to the end of the allocate list and initialize the core 
  316.      * map entry.  If page is for a kernel process then don't put it onto
  317.      * the end of the allocate list.
  318.      */
  319.     if (virtAddrPtr->segPtr != vm_SysSegPtr) {
  320.     PutOnAllocListRear(corePtr);
  321.     }
  322.  
  323.     corePtr->virtPage = *virtAddrPtr;
  324.     /*
  325.      * Change page numbering from segOffset base to segPtr->offset base.
  326.      * This is so the number is constant with shared memory.
  327.      * The problem is that to convert to a page table offset, we take
  328.      * the page # - the start of the page table.  Normally we use segOffset
  329.      * to get the appropriate offset for the segment, which may be mapped
  330.      * into different places.  However, this screws up the corePtr's value.
  331.      * So we fix it to be an index from segPtr->offset, which doesn't depend
  332.      * on the shared memory mapping.
  333.      */
  334.     corePtr->virtPage.page = corePtr->virtPage.page - segOffset(virtAddrPtr)
  335.         + virtAddrPtr->segPtr->offset;
  336.     corePtr->virtPage.sharedPtr = (Vm_SegProcList *)NIL;
  337.     corePtr->flags = 0;
  338.     corePtr->lockCount = 1;
  339.     corePtr->lastRef = curTime.seconds;
  340.  
  341.     UNLOCK_MONITOR;
  342. }
  343.  
  344.  
  345. /*
  346.  * ----------------------------------------------------------------------------
  347.  *
  348.  * TakeOffAllocList --
  349.  *
  350.  *         Take this core map entry off of the allocate list
  351.  *
  352.  * Results:
  353.  *         None.
  354.  *
  355.  * Side effects:
  356.  *    Alloc list modified and core map entry modified.
  357.  * ----------------------------------------------------------------------------
  358.  */
  359. INTERNAL static void
  360. TakeOffAllocList(corePtr)
  361.     VmCore    *corePtr;
  362. {
  363.     VmListRemove((List_Links *) corePtr);
  364.     vmStat.numUserPages--;
  365. }
  366.  
  367.  
  368. /*
  369.  * ----------------------------------------------------------------------------
  370.  *
  371.  * PutOnReserveList --
  372.  *
  373.  *         Put this core map entry onto the reserve page list.
  374.  *
  375.  * Results:
  376.  *         None.
  377.  *
  378.  * Side effects:
  379.  *    Reserve list modified and core map entry modified.
  380.  * ----------------------------------------------------------------------------
  381.  */
  382. INTERNAL static void
  383. PutOnReserveList(corePtr)
  384.     register    VmCore    *corePtr;
  385. {
  386.     corePtr->flags = 0;
  387.     corePtr->lockCount = 1;
  388.     VmListInsert((List_Links *) corePtr, LIST_ATREAR(reservePageList));
  389.     vmStat.numReservePages++;
  390. }
  391.  
  392.  
  393. /*
  394.  * ----------------------------------------------------------------------------
  395.  *
  396.  * VmGetReservePage --
  397.  *
  398.  *         Take a core map entry off of the reserve list and return its 
  399.  *    page frame number.
  400.  *
  401.  * Results:
  402.  *         None.
  403.  *
  404.  * Side effects:
  405.  *    Reserve list modified and core map entry modified.
  406.  * ----------------------------------------------------------------------------
  407.  */
  408. INTERNAL unsigned int
  409. VmGetReservePage(virtAddrPtr)
  410.     Vm_VirtAddr    *virtAddrPtr;
  411. {
  412.     VmCore    *corePtr;
  413.  
  414.     if (List_IsEmpty(reservePageList)) {
  415.     return(VM_NO_MEM_VAL);
  416.     }
  417.     printf("Taking from reserve list\n");
  418.     vmStat.reservePagesUsed++;
  419.     corePtr = (VmCore *) List_First(reservePageList);
  420.     List_Remove((List_Links *) corePtr);
  421.     vmStat.numReservePages--;
  422.     corePtr->virtPage = *virtAddrPtr;
  423.     /*
  424.      * Change page numbering from segOffset base to segPtr->offset base.
  425.      * This is so the number is constant with shared memory.
  426.      */
  427.     corePtr->virtPage.page = corePtr->virtPage.page - segOffset(virtAddrPtr)
  428.         + virtAddrPtr->segPtr->offset;
  429.     corePtr->virtPage.sharedPtr = (Vm_SegProcList *)NIL;
  430.  
  431.     return(corePtr - coreMap);
  432. }
  433.  
  434.  
  435. /*
  436.  * ----------------------------------------------------------------------------
  437.  *
  438.  * PutOnFreeList --
  439.  *
  440.  *         Put this core map entry onto the free list.  The page will actually
  441.  *    end up on the reserve list if the reserve list needs more pages.
  442.  *
  443.  * Results:
  444.  *         None.
  445.  *
  446.  * Side effects:
  447.  *    Free list or reserve list modified and core map entry modified.
  448.  * ----------------------------------------------------------------------------
  449.  */
  450. INTERNAL static void
  451. PutOnFreeList(corePtr)
  452.     register    VmCore    *corePtr;
  453. {
  454.     if (vmStat.numReservePages < NUM_RESERVE_PAGES) {
  455.     PutOnReserveList(corePtr);
  456.     } else {
  457.     corePtr->flags = VM_FREE_PAGE;
  458.     corePtr->lockCount = 0;
  459.     VmListInsert((List_Links *) corePtr, LIST_ATREAR(freePageList));
  460.     vmStat.numFreePages++;
  461.     }
  462. }
  463.  
  464.  
  465. /*
  466.  * ----------------------------------------------------------------------------
  467.  *
  468.  * TakeOffFreeList --
  469.  *
  470.  *         Take this core map entry off of the free list.
  471.  *
  472.  * Results:
  473.  *         None.
  474.  *
  475.  * Side effects:
  476.  *    Free list modified and core map entry modified.
  477.  * ----------------------------------------------------------------------------
  478.  */
  479. INTERNAL static void
  480. TakeOffFreeList(corePtr)
  481.     VmCore    *corePtr;
  482. {
  483.     VmListRemove((List_Links *) corePtr);
  484.     vmStat.numFreePages--;
  485. }
  486.  
  487.  
  488. /*
  489.  * ----------------------------------------------------------------------------
  490.  *
  491.  * VmPutOnFreePageList --
  492.  *
  493.  *      Put the given page frame onto the free list.
  494.  *
  495.  * Results:
  496.  *         None.
  497.  *
  498.  * Side effects:
  499.  *         None.
  500.  *
  501.  * ----------------------------------------------------------------------------
  502.  */
  503. INTERNAL void
  504. VmPutOnFreePageList(pfNum)
  505.     unsigned    int    pfNum;        /* The page frame to be freed. */
  506. {
  507.     if (pfNum == 0) {
  508.     /*
  509.      * Page frame number 0 is special because a page frame of 0 on a
  510.      * user page fault has special meaning.  Thus if the kernel decides
  511.      * to free page frame 0 then we can't make this page eligible for user
  512.      * use.  Instead of throwing it away put it onto the reserve list
  513.      * because only the kernel uses pages on the reserve list.
  514.      */
  515.     PutOnReserveList(&coreMap[pfNum]);
  516.     } else {
  517.     PutOnFreeList(&coreMap[pfNum]);
  518.     }
  519. }
  520.  
  521.  
  522. /*
  523.  * ----------------------------------------------------------------------------
  524.  *
  525.  * PutOnDirtyList --
  526.  *
  527.  *    Put the given core map entry onto the dirty list and wakeup the page
  528.  *    out daemon.
  529.  *
  530.  * Results:
  531.  *         None.
  532.  *
  533.  * Side effects:
  534.  *         Page added to dirty list, number of dirty pages is incremented and 
  535.  *    number of active page out processes may be incremented.
  536.  *
  537.  * ----------------------------------------------------------------------------
  538.  */
  539. INTERNAL static void
  540. PutOnDirtyList(corePtr)
  541.     register    VmCore    *corePtr;
  542. {
  543.     vmStat.numDirtyPages++;
  544.     VmListInsert((List_Links *) corePtr, LIST_ATREAR(dirtyPageList));
  545.     corePtr->flags |= VM_DIRTY_PAGE;
  546.     if (vmStat.numDirtyPages - numPageOutProcs > 0 &&
  547.     numPageOutProcs < vmMaxPageOutProcs) { 
  548.     Proc_CallFunc(PageOut, (ClientData) numPageOutProcs, 0);
  549.     numPageOutProcs++;
  550.     }
  551. }
  552.  
  553.  
  554. /*
  555.  * ----------------------------------------------------------------------------
  556.  *
  557.  * TakeOffDirtyList --
  558.  *
  559.  *         Take this core map entry off of the dirty list.
  560.  *
  561.  * Results:
  562.  *         None.
  563.  *
  564.  * Side effects:
  565.  *    Dirty list modified and core map entry modified.
  566.  * ----------------------------------------------------------------------------
  567.  */
  568. INTERNAL static void
  569. TakeOffDirtyList(corePtr)
  570.     VmCore    *corePtr;
  571. {
  572.     VmListRemove((List_Links *) corePtr);
  573.     vmStat.numDirtyPages--;
  574. }
  575.  
  576.  
  577. /*
  578.  * ----------------------------------------------------------------------------
  579.  *
  580.  * VmPutOnDirtyList --
  581.  *
  582.  *         Put the given page onto the front of the dirty list.  It is assumed
  583.  *    the page is currently on either the allocate list or the dirty list.
  584.  *    In either case mark the page such that it will not get freed until
  585.  *    it is written out.
  586.  *
  587.  * Results:
  588.  *         None.
  589.  *
  590.  * Side effects:
  591.  *         The dirty list is modified.
  592.  *
  593.  * ----------------------------------------------------------------------------
  594.  */
  595. ENTRY void
  596. VmPutOnDirtyList(pfNum)
  597.     unsigned    int    pfNum;
  598. {
  599.     register    VmCore    *corePtr; 
  600.  
  601.     LOCK_MONITOR;
  602.  
  603.     corePtr = &(coreMap[pfNum]);
  604.     if (!(corePtr->flags & VM_DIRTY_PAGE)) {
  605.     TakeOffAllocList(corePtr);
  606.     PutOnDirtyList(corePtr);
  607.     }
  608.     corePtr->flags |= VM_DONT_FREE_UNTIL_CLEAN;
  609.  
  610.     UNLOCK_MONITOR;
  611. }
  612.  
  613.  
  614. /*
  615.  * ----------------------------------------------------------------------------
  616.  *
  617.  *    Routines to validate and invalidate pages.
  618.  *
  619.  * ----------------------------------------------------------------------------
  620.  */
  621.  
  622.  
  623. /*
  624.  * ----------------------------------------------------------------------------
  625.  *
  626.  * VmPageValidate --
  627.  *
  628.  *         Validate the page at the given virtual address.
  629.  *
  630.  * Results:
  631.  *         None.
  632.  *
  633.  * Side effects:
  634.  *    None.
  635.  * ----------------------------------------------------------------------------
  636.  */
  637. ENTRY void
  638. VmPageValidate(virtAddrPtr)
  639.     Vm_VirtAddr    *virtAddrPtr;
  640. {
  641.     LOCK_MONITOR;
  642.  
  643.     VmPageValidateInt(virtAddrPtr, 
  644.               VmGetAddrPTEPtr(virtAddrPtr, virtAddrPtr->page));
  645.  
  646.     UNLOCK_MONITOR;
  647. }
  648.  
  649.  
  650. /*
  651.  * ----------------------------------------------------------------------------
  652.  *
  653.  * VmPageValidateInt --
  654.  *
  655.  *         Validate the page at the given virtual address.
  656.  *
  657.  * Results:
  658.  *         None.
  659.  *
  660.  * Side effects:
  661.  *    Page table modified.
  662.  * ----------------------------------------------------------------------------
  663.  */
  664. INTERNAL void
  665. VmPageValidateInt(virtAddrPtr, ptePtr)
  666.     Vm_VirtAddr        *virtAddrPtr;
  667.     register    Vm_PTE    *ptePtr;
  668. {
  669.     if  (!(*ptePtr & VM_PHYS_RES_BIT)) {
  670.     virtAddrPtr->segPtr->resPages++;
  671.     *ptePtr |= VM_PHYS_RES_BIT;
  672.     }
  673.     VmMach_PageValidate(virtAddrPtr, *ptePtr);
  674. }
  675.  
  676.  
  677. /*
  678.  * ----------------------------------------------------------------------------
  679.  *
  680.  * VmPageInvalidate --
  681.  *
  682.  *         Invalidate the page at the given virtual address.
  683.  *
  684.  * Results:
  685.  *         None.
  686.  *
  687.  * Side effects:
  688.  *    None.
  689.  * ----------------------------------------------------------------------------
  690.  */
  691. ENTRY void
  692. VmPageInvalidate(virtAddrPtr)
  693.     register    Vm_VirtAddr    *virtAddrPtr;
  694. {
  695.     LOCK_MONITOR;
  696.  
  697.     VmPageInvalidateInt(virtAddrPtr, 
  698.     VmGetAddrPTEPtr(virtAddrPtr, virtAddrPtr->page));
  699.  
  700.     UNLOCK_MONITOR;
  701. }
  702.  
  703.  
  704. /*
  705.  * ----------------------------------------------------------------------------
  706.  *
  707.  * VmPageInvalidateInt --
  708.  *
  709.  *         Invalidate the page at the given virtual address.
  710.  *
  711.  * Results:
  712.  *         None.
  713.  *
  714.  * Side effects:
  715.  *    Page table modified.
  716.  * ----------------------------------------------------------------------------
  717.  */
  718. INTERNAL void
  719. VmPageInvalidateInt(virtAddrPtr, ptePtr)
  720.     Vm_VirtAddr        *virtAddrPtr;
  721.     register    Vm_PTE    *ptePtr;
  722. {
  723.     if (*ptePtr & VM_PHYS_RES_BIT) {
  724.     virtAddrPtr->segPtr->resPages--;
  725.     VmMach_PageInvalidate(virtAddrPtr, Vm_GetPageFrame(*ptePtr), FALSE);
  726.     *ptePtr &= ~(VM_PHYS_RES_BIT | VM_PAGE_FRAME_FIELD);
  727.     }
  728. }
  729.  
  730.  
  731. /*
  732.  * ----------------------------------------------------------------------------
  733.  *
  734.  *    Routines to lock and unlock pages.
  735.  *
  736.  * ----------------------------------------------------------------------------
  737.  */
  738.  
  739. /*
  740.  * ----------------------------------------------------------------------------
  741.  *
  742.  * VmLockPageInt --
  743.  *
  744.  *         Increment the lock count on a page.
  745.  *
  746.  * Results:
  747.  *         None.
  748.  *
  749.  * Side effects:
  750.  *    The core map entry for the page has its lock count incremented.
  751.  *
  752.  * ----------------------------------------------------------------------------
  753.  */
  754. INTERNAL void
  755. VmLockPageInt(pfNum)
  756.     unsigned    int        pfNum;
  757. {
  758.     coreMap[pfNum].lockCount++;
  759. }
  760.  
  761.  
  762. /*
  763.  * ----------------------------------------------------------------------------
  764.  *
  765.  * VmUnlockPage --
  766.  *
  767.  *         Decrement the lock count on a page.
  768.  *
  769.  * Results:
  770.  *         None.
  771.  *
  772.  * Side effects:
  773.  *    The core map entry for the page has its lock count decremented.
  774.  *
  775.  * ----------------------------------------------------------------------------
  776.  */
  777. ENTRY void
  778. VmUnlockPage(pfNum)
  779.     unsigned    int    pfNum;
  780. {
  781.     LOCK_MONITOR;
  782.     coreMap[pfNum].lockCount--;
  783.     if (coreMap[pfNum].lockCount < 0) {
  784.     panic("VmUnlockPage: Coremap lock count < 0\n");
  785.     }
  786.     UNLOCK_MONITOR;
  787. }
  788.  
  789.  
  790. /*
  791.  * ----------------------------------------------------------------------------
  792.  *
  793.  * VmUnlockPageInt --
  794.  *
  795.  *         Decrement the lock count on a page.
  796.  *
  797.  * Results:
  798.  *         None.
  799.  *
  800.  * Side effects:
  801.  *    The core map entry for the page has its lock count decremented.
  802.  *
  803.  * ----------------------------------------------------------------------------
  804.  */
  805. INTERNAL void
  806. VmUnlockPageInt(pfNum)
  807.     unsigned    int    pfNum;
  808. {
  809.     coreMap[pfNum].lockCount--;
  810.     if (coreMap[pfNum].lockCount < 0) {
  811.     panic("VmUnlockPage: Coremap lock count < 0\n");
  812.     }
  813. }
  814.  
  815.  
  816. /*
  817.  * ----------------------------------------------------------------------------
  818.  *
  819.  * VmPageSwitch --
  820.  *
  821.  *         Move the given page from the current owner to the new owner.
  822.  *
  823.  * Results:
  824.  *         None.
  825.  *
  826.  * Side effects:
  827.  *    The segment pointer int the core map entry for the page is modified and
  828.  *    the page is unlocked.
  829.  *
  830.  * ----------------------------------------------------------------------------
  831.  */
  832. INTERNAL void
  833. VmPageSwitch(pageNum, newSegPtr)
  834.     unsigned    int    pageNum;
  835.     Vm_Segment        *newSegPtr;
  836. {
  837.     coreMap[pageNum].virtPage.segPtr = newSegPtr;
  838.     coreMap[pageNum].lockCount--;
  839. }
  840.  
  841.  
  842. /*
  843.  * ----------------------------------------------------------------------------
  844.  *
  845.  *    Routines to get reference times of VM pages.
  846.  *
  847.  * ----------------------------------------------------------------------------
  848.  */
  849.  
  850. /*
  851.  * ----------------------------------------------------------------------------
  852.  *
  853.  * Vm_GetRefTime --
  854.  *
  855.  *         Return the age of the LRU page (0 if is a free page).
  856.  *
  857.  * Results:
  858.  *         Age of LRU page (0 if there is a free page).
  859.  *
  860.  * Side effects:
  861.  *         None.
  862.  *
  863.  * ----------------------------------------------------------------------------
  864.  */
  865. ENTRY int
  866. Vm_GetRefTime()
  867. {
  868.     register    VmCore    *corePtr; 
  869.     int            refTime;
  870.  
  871.     LOCK_MONITOR;
  872.  
  873.     vmStat.fsAsked++;
  874.  
  875.     if (swapDown || (vmStat.numFreePages + vmStat.numUserPages + 
  876.              vmStat.numDirtyPages <= vmStat.minVMPages)) {
  877.     /*
  878.      * We are already at or below the minimum amount of memory that
  879.      * we are guaranteed for our use so refuse to give any memory to
  880.      * the file system.
  881.      */
  882.     UNLOCK_MONITOR;
  883.     return((int) 0x7fffffff);
  884.     }
  885.  
  886.     if (!List_IsEmpty(freePageList)) {
  887.     vmStat.haveFreePage++;
  888.     refTime = 0;
  889.     if (vmDebug) {
  890.         printf("Vm_GetRefTime: VM has free page\n");
  891.     }
  892.     } else {
  893.     refTime = (int) 0x7fffffff;
  894.     if (!List_IsEmpty(dirtyPageList)) {
  895.         corePtr = (VmCore *) List_First(dirtyPageList);
  896.         refTime = corePtr->lastRef;
  897.     }
  898.     if (!List_IsEmpty(allocPageList)) {
  899.         corePtr = (VmCore *) List_First(allocPageList);
  900.         if (corePtr->lastRef < refTime) {
  901.         refTime = corePtr->lastRef;
  902.         }
  903.     }
  904.     if (vmDebug) {
  905.         printf("Vm_GetRefTime: Reftime = %d\n", refTime);
  906.     }
  907.     }
  908.  
  909.     if (vmAlwaysRefuse) {
  910.     refTime = INT_MAX;
  911.     } else if (vmAlwaysSayYes) {
  912.     refTime = 0;
  913.     } else {
  914.     refTime += vmCurPenalty;
  915.     }
  916.  
  917.     UNLOCK_MONITOR;
  918.  
  919.     return(refTime);
  920. }
  921.  
  922. /*
  923.  * ----------------------------------------------------------------------------
  924.  *
  925.  * GetRefTime --
  926.  *
  927.  *         Return either the first free page on the allocate list or the
  928.  *    last reference time of the first page on the list.
  929.  *
  930.  * Results:
  931.  *         None.
  932.  *
  933.  * Side effects:
  934.  *         First page removed from allocate list if one is free.
  935.  *
  936.  * ----------------------------------------------------------------------------
  937.  */
  938. ENTRY static void
  939. GetRefTime(refTimePtr, pagePtr)
  940.     register    int    *refTimePtr;
  941.     unsigned    int    *pagePtr;
  942. {
  943.     register    VmCore    *corePtr; 
  944.  
  945.     LOCK_MONITOR;
  946.  
  947.     if (!List_IsEmpty(freePageList)) {
  948.     vmStat.gotFreePage++;
  949.     corePtr = (VmCore *) List_First(freePageList);
  950.     TakeOffFreeList(corePtr);
  951.     *pagePtr = corePtr - coreMap;
  952.     } else {
  953.     *refTimePtr = (int) 0x7fffffff;
  954.     if (!List_IsEmpty(dirtyPageList)) {
  955.         corePtr = (VmCore *) List_First(dirtyPageList);
  956.         *refTimePtr = corePtr->lastRef;
  957.     }
  958.     if (!List_IsEmpty(allocPageList)) {
  959.         corePtr = (VmCore *) List_First(allocPageList);
  960.         if (corePtr->lastRef < *refTimePtr) {
  961.         *refTimePtr = corePtr->lastRef;
  962.         }
  963.     }
  964.     *pagePtr = VM_NO_MEM_VAL;
  965.     }
  966.  
  967.     UNLOCK_MONITOR;
  968. }
  969.  
  970. /*
  971.  * ----------------------------------------------------------------------------
  972.  *
  973.  *    Routines to allocate pages.
  974.  *
  975.  * ----------------------------------------------------------------------------
  976.  */
  977.  
  978. /*
  979.  * ----------------------------------------------------------------------------
  980.  *
  981.  * DoPageAllocate --
  982.  *
  983.  *         Grab the monitor lock and call VmPageAllocate.
  984.  *
  985.  * Results:
  986.  *         The virtual page number that is allocated.
  987.  *
  988.  * Side effects:
  989.  *         None.
  990.  *
  991.  * ----------------------------------------------------------------------------
  992.  */
  993. ENTRY static unsigned    int
  994. DoPageAllocate(virtAddrPtr, flags)
  995.     Vm_VirtAddr    *virtAddrPtr;    /* The translated virtual address that 
  996.                    indicates the segment and virtual page 
  997.                    that this physical page is being allocated 
  998.                    for */
  999.     int        flags;        /* VM_CAN_BLOCK | VM_ABORT_WHEN_DIRTY */
  1000. {
  1001.     unsigned    int page;
  1002.  
  1003.     LOCK_MONITOR;
  1004.  
  1005.     while (swapDown) {
  1006.     (void)Sync_Wait(&swapDownCondition, FALSE);
  1007.     }
  1008.     page = VmPageAllocateInt(virtAddrPtr, flags);
  1009.  
  1010.     UNLOCK_MONITOR;
  1011.     return(page);
  1012. }
  1013.  
  1014.  
  1015. /*
  1016.  * ----------------------------------------------------------------------------
  1017.  *
  1018.  * VmPageAllocate --
  1019.  *
  1020.  *         Return a page frame.  Will either get a page from VM or FS depending
  1021.  *    on the LRU comparison and if there is a free page or not.
  1022.  *
  1023.  * Results:
  1024.  *         The page frame number that is allocated.
  1025.  *
  1026.  * Side effects:
  1027.  *         None.
  1028.  *
  1029.  * ----------------------------------------------------------------------------
  1030.  */
  1031. unsigned int
  1032. VmPageAllocate(virtAddrPtr, flags)
  1033.     Vm_VirtAddr    *virtAddrPtr;    /* The translated virtual address that this
  1034.                  * page frame is being allocated for */
  1035.     int        flags;        /* VM_CAN_BLOCK | VM_ABORT_WHEN_DIRTY. */
  1036. {
  1037.     unsigned    int    page;
  1038.     int            refTime;
  1039.     int            tPage;
  1040.  
  1041.     vmStat.numAllocs++;
  1042.  
  1043.     GetRefTime(&refTime, &page);
  1044.     if (page == VM_NO_MEM_VAL) {
  1045.     Fscache_GetPageFromFS(refTime + vmCurPenalty, &tPage);
  1046.     if (tPage == -1) {
  1047.         vmStat.pageAllocs++;
  1048.         return(DoPageAllocate(virtAddrPtr, flags));
  1049.     } else {
  1050.         page = tPage;
  1051.         vmStat.gotPageFromFS++;
  1052.         if (vmDebug) {
  1053.         printf("VmPageAllocate: Took page from FS (refTime = %d)\n",
  1054.                 refTime);
  1055.         }
  1056.     }
  1057.     }
  1058.  
  1059.     /*
  1060.      * Move the page to the end of the allocate list and initialize the core 
  1061.      * map entry.
  1062.      */
  1063.     PutOnAllocList(virtAddrPtr, page);
  1064.  
  1065.     return(page);
  1066. }
  1067.  
  1068.  
  1069. /*
  1070.  * ----------------------------------------------------------------------------
  1071.  *
  1072.  * VmPageAllocateInt --
  1073.  *
  1074.  *         This routine will return the page frame number of the first free or
  1075.  *         unreferenced, unmodified, unlocked page that it can find on the 
  1076.  *    allocate list.  The core map entry for this page will be initialized to 
  1077.  *    contain the virtual page number and the lock count will be set to 
  1078.  *    1 to indicate that this page is locked down.
  1079.  *
  1080.  *    This routine will sleep if the entire allocate list is dirty in order
  1081.  *    to give the page-out daemon some time to clean pages.
  1082.  *
  1083.  * Results:
  1084.  *         The physical page number that is allocated.
  1085.  *
  1086.  * Side effects:
  1087.  *         The allocate list is modified and the  dirty list may be modified.
  1088.  *    In addition the appropriate core map entry is initialized.
  1089.  *
  1090.  * ----------------------------------------------------------------------------
  1091.  */
  1092. INTERNAL unsigned int
  1093. VmPageAllocateInt(virtAddrPtr, flags)
  1094.     Vm_VirtAddr    *virtAddrPtr;    /* The translated virtual address that 
  1095.                    this page frame is being allocated for */
  1096.     int        flags;        /* VM_CAN_BLOCK if can block if non memory is
  1097.                  * available. VM_ABORT_WHEN_DIRTY if should
  1098.                  * abort even if VM_CAN_BLOCK is set if have
  1099.                  * exceeded the maximum number of dirty pages
  1100.                  * on the dirty list. */
  1101. {
  1102.     register    VmCore    *corePtr; 
  1103.     register    Vm_PTE    *ptePtr;
  1104.     Time        curTime;
  1105.     List_Links        endMarker;
  1106.     Boolean        referenced;
  1107.     Boolean        modified;
  1108.  
  1109.     Timer_GetTimeOfDay(&curTime, (int *) NIL, (Boolean *) NIL);
  1110.  
  1111.     vmStat.numListSearches++;
  1112.  
  1113. again:
  1114.     if (!List_IsEmpty(freePageList)) {
  1115.     corePtr = (VmCore *) List_First(freePageList);
  1116.     TakeOffFreeList(corePtr);
  1117.     vmStat.usedFreePage++;
  1118.     } else {
  1119.     /*
  1120.      * Put a marker at the end of the core map so that we can detect loops.
  1121.      */
  1122.     endMarker.nextPtr = (List_Links *) NIL;
  1123.     endMarker.prevPtr = (List_Links *) NIL;
  1124.     VmListInsert(&endMarker, LIST_ATREAR(allocPageList));
  1125.  
  1126.     /*
  1127.      * Loop examining the page on the front of the allocate list until 
  1128.      * a free or unreferenced, unmodified, unlocked page frame is found.
  1129.      * If the whole list is examined and nothing found, then return 
  1130.      * VM_NO_MEM_VAL.
  1131.      */
  1132.     while (TRUE) {
  1133.         corePtr = (VmCore *) List_First(allocPageList);
  1134.  
  1135.         /*
  1136.          * See if have gone all of the way through the list without finding
  1137.          * anything.
  1138.          */
  1139.         if (((flags & (VM_CAN_BLOCK | VM_ABORT_WHEN_DIRTY)) && 
  1140.              vmStat.numDirtyPages > vmMaxDirtyPages) ||
  1141.             corePtr == (VmCore *) &endMarker) {    
  1142.         VmListRemove((List_Links *) &endMarker);
  1143.         if (!(flags & VM_CAN_BLOCK)) {
  1144.             return(VM_NO_MEM_VAL);
  1145.         } else {
  1146.             /*
  1147.              * There were no pages available.  Wait for a clean
  1148.              * page to appear on the allocate list.
  1149.              */
  1150.             (void)Sync_Wait(&cleanCondition, FALSE);
  1151.             goto again;
  1152.         }
  1153.         }
  1154.     
  1155.         /*
  1156.          * Make sure that the page is not locked down.
  1157.          */
  1158.         if (corePtr->lockCount > 0) {
  1159.         vmStat.lockSearched++;
  1160.         VmListMove((List_Links *) corePtr, LIST_ATREAR(allocPageList));
  1161.         continue;
  1162.         }
  1163.  
  1164.         ptePtr = VmGetAddrPTEPtr(&corePtr->virtPage,
  1165.                  corePtr->virtPage.page);
  1166.         referenced = *ptePtr & VM_REFERENCED_BIT;
  1167.         modified = *ptePtr & VM_MODIFIED_BIT;
  1168.         VmMach_AllocCheck(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr),
  1169.                   &referenced, &modified);
  1170.         /*
  1171.          * Now make sure that the page has not been referenced.  It it has
  1172.          * then clear the reference bit and put it onto the end of the
  1173.          * allocate list.
  1174.          */
  1175.         if (referenced) {
  1176.         vmStat.refSearched++;
  1177.         corePtr->lastRef = curTime.seconds;
  1178.         *ptePtr &= ~VM_REFERENCED_BIT;
  1179.         VmMach_ClearRefBit(&corePtr->virtPage,
  1180.                    Vm_GetPageFrame(*ptePtr));
  1181.         if (vmWriteableRefPageout &&
  1182.             corePtr->virtPage.segPtr->type != VM_CODE) {
  1183.             *ptePtr |= VM_MODIFIED_BIT;
  1184.         }
  1185.  
  1186.         VmListMove((List_Links *) corePtr, LIST_ATREAR(allocPageList));
  1187.         /*
  1188.          * Set the last page marker so that we will try to examine this
  1189.          * page again if we go all the way around without finding 
  1190.          * anything.  
  1191.          *
  1192.          * NOTE: This is only a uni-processor solution since
  1193.          *       on a multi-processor a process could be continually 
  1194.          *       touching pages while we are scanning the list.
  1195.          */
  1196.         VmListMove(&endMarker, LIST_ATREAR(allocPageList));
  1197.         continue;
  1198.         }
  1199.  
  1200.         if (corePtr->virtPage.segPtr->type != VM_CODE) {
  1201.         vmStat.potModPages++;
  1202.         }
  1203.         /*
  1204.          * The page is available and it has not been referenced.  Now
  1205.          * it must be determined if it is dirty.
  1206.          */
  1207.         if (modified) {
  1208.         /*
  1209.          * Dirty pages go onto the dirty list.
  1210.          */
  1211.         vmStat.dirtySearched++;
  1212.         TakeOffAllocList(corePtr);
  1213.         PutOnDirtyList(corePtr);
  1214.         continue;
  1215.         }
  1216.  
  1217.         if (corePtr->virtPage.segPtr->type != VM_CODE) {
  1218.         vmStat.notModPages++;
  1219.         }
  1220.         /*
  1221.          * We can take this page.  Invalidate the page for the old segment.
  1222.          * VmMach_AllocCheck will have already invalidated the page for
  1223.          * us in hardware.
  1224.          */
  1225.         corePtr->virtPage.segPtr->resPages--;
  1226.         *ptePtr &= ~(VM_PHYS_RES_BIT | VM_PAGE_FRAME_FIELD);
  1227.  
  1228.         TakeOffAllocList(corePtr);
  1229.         VmListRemove(&endMarker);
  1230.         break;
  1231.     }
  1232.     }
  1233.  
  1234.     /*
  1235.      * If this page is being allocated for the kernel segment then don't put
  1236.      * it back onto the allocate list because kernel pages don't exist on
  1237.      * the allocate list.  Otherwise move it to the rear of the allocate list.
  1238.      */
  1239.     if (virtAddrPtr->segPtr != vm_SysSegPtr) {
  1240.     PutOnAllocListRear(corePtr);
  1241.     }
  1242.     corePtr->virtPage = *virtAddrPtr;
  1243.     /*
  1244.      * Change page numbering from segOffset base to segPtr->offset base.
  1245.      * This is so the number is constant with shared memory.
  1246.      */
  1247.     corePtr->virtPage.page = corePtr->virtPage.page - segOffset(virtAddrPtr)
  1248.         + virtAddrPtr->segPtr->offset;
  1249.     corePtr->virtPage.sharedPtr = (Vm_SegProcList *)NIL;
  1250.     corePtr->flags = 0;
  1251.     corePtr->lockCount = 1;
  1252.     corePtr->wireCount = 0;
  1253.     corePtr->lastRef = curTime.seconds;
  1254.  
  1255.     return(corePtr - coreMap);
  1256. }
  1257.  
  1258. /*
  1259.  * ----------------------------------------------------------------------------
  1260.  *
  1261.  * VmPageFreeInt --
  1262.  *
  1263.  *      This routine will put the given page frame onto the front of the
  1264.  *      free list if it is not on the dirty list.  If the page frame is on 
  1265.  *    the dirty list then this routine will sleep until the page has been
  1266.  *    cleaned.  The page-out daemon will put the page onto the front of the
  1267.  *    allocate list when it finishes cleaning the page.
  1268.  *
  1269.  * Results:
  1270.  *         None.
  1271.  *
  1272.  * Side effects:
  1273.  *         The free list is modified and the core map entry is set to free
  1274.  *    with a lockcount of 0.
  1275.  *
  1276.  * ----------------------------------------------------------------------------
  1277.  */
  1278. INTERNAL void
  1279. VmPageFreeInt(pfNum)
  1280.     unsigned    int    pfNum;        /* The page frame to be freed. */
  1281. {
  1282.     register    VmCore    *corePtr; 
  1283.  
  1284.     corePtr = &(coreMap[pfNum]);
  1285.  
  1286.     corePtr->flags |= VM_FREE_PAGE;
  1287.     corePtr->lockCount = 0;
  1288.  
  1289.     if (corePtr->virtPage.segPtr == vm_SysSegPtr) {
  1290.         /*
  1291.      * Pages given to the kernel are removed from the allocate list when
  1292.      * they are allocated.  Therefore just put it back onto the free list.
  1293.      */
  1294.     if (corePtr->flags & (VM_DIRTY_PAGE | VM_PAGE_BEING_CLEANED)) {
  1295.         panic("VmPageFreeInt: Kernel page on dirty list\n");
  1296.     }
  1297.     PutOnFreeList(corePtr);
  1298.     } else {
  1299.     /*
  1300.      * If the page is being written then wait for it to finish.
  1301.      * Once it has been cleaned it will automatically be put onto the free
  1302.      * list.  We must wait for it to be cleaned because 
  1303.      * the segment may die otherwise while the page is still waiting to be 
  1304.      * cleaned.  This would be a disaster because the page-out daemon uses
  1305.      * the segment table entry to determine where to write the page.
  1306.      */
  1307.     if (corePtr->flags & 
  1308.             (VM_PAGE_BEING_CLEANED | VM_DONT_FREE_UNTIL_CLEAN)) {
  1309.         do {
  1310.         corePtr->flags |= VM_SEG_PAGEOUT_WAIT;
  1311.         vmStat.cleanWait++;
  1312.         (void) Sync_Wait(&corePtr->virtPage.segPtr->condition, FALSE);
  1313.         } while (corePtr->flags & 
  1314.             (VM_PAGE_BEING_CLEANED | VM_DONT_FREE_UNTIL_CLEAN));
  1315.     } else {
  1316.         if (corePtr->flags & VM_DIRTY_PAGE) {
  1317.         TakeOffDirtyList(corePtr);
  1318.         } else {
  1319.         TakeOffAllocList(corePtr);
  1320.         }
  1321.         PutOnFreeList(corePtr);
  1322.     }
  1323.     }
  1324. }
  1325.  
  1326.  
  1327. /*
  1328.  * ----------------------------------------------------------------------------
  1329.  *
  1330.  * VmPageFree --
  1331.  *
  1332.  *      Free the given page.  Call VmPageFreeInt to do the work.
  1333.  *
  1334.  * Results:
  1335.  *         None.
  1336.  *
  1337.  * Side effects:
  1338.  *         None.
  1339.  *
  1340.  * ----------------------------------------------------------------------------
  1341.  */
  1342. ENTRY void
  1343. VmPageFree(pfNum)
  1344.     unsigned    int    pfNum;        /* The page frame to be freed. */
  1345. {
  1346.     LOCK_MONITOR;
  1347.  
  1348.     VmPageFreeInt(pfNum);
  1349.  
  1350.     UNLOCK_MONITOR;
  1351. }
  1352.  
  1353.  
  1354. /*
  1355.  * ----------------------------------------------------------------------------
  1356.  *
  1357.  * Vm_ReservePage --
  1358.  *
  1359.  *      Take a page out of the available pages because this page is
  1360.  *    being used by the hardware dependent module.  This routine is
  1361.  *    called at boot time.
  1362.  *
  1363.  * Results:
  1364.  *         None.
  1365.  *
  1366.  * Side effects:
  1367.  *         None.
  1368.  *
  1369.  * ----------------------------------------------------------------------------
  1370.  */
  1371. ENTRY void
  1372. Vm_ReservePage(pfNum)
  1373.     unsigned    int    pfNum;        /* The page frame to be freed. */
  1374. {
  1375.     register    VmCore    *corePtr;
  1376.  
  1377.     LOCK_MONITOR;
  1378.  
  1379.     if (pfNum < vmStat.numPhysPages) {
  1380.     corePtr = &coreMap[pfNum];
  1381.     TakeOffFreeList(corePtr);
  1382.     corePtr->virtPage.segPtr = vm_SysSegPtr;
  1383.     corePtr->flags = 0;
  1384.     corePtr->lockCount = 1;
  1385.     }
  1386.  
  1387.     UNLOCK_MONITOR;
  1388. }
  1389.         
  1390.  
  1391. /*-----------------------------------------------------------------------
  1392.  *
  1393.  *         Routines to handle page faults.
  1394.  *
  1395.  * Page fault handling is divided into three routines.  The first
  1396.  * routine is Vm_PageIn.  It calls two monitored routines PreparePage and
  1397.  * FinishPage to do most of the monitor level work.
  1398.  */
  1399.  
  1400. typedef enum {
  1401.     IS_COR,    /* This page is copy-on-reference. */
  1402.     IS_COW,     /* This page is copy-on-write. */
  1403.     IS_DONE,     /* The page-in has already completed. */
  1404.     NOT_DONE,    /* The page-in is not yet done yet. */
  1405. } PrepareResult;
  1406.  
  1407. static PrepareResult PreparePage _ARGS_((register Vm_VirtAddr *virtAddrPtr, Boolean protFault, register Vm_PTE *curPTEPtr));
  1408. static void FinishPage _ARGS_((register Vm_VirtAddr *transVirtAddrPtr, register Vm_PTE *ptePtr));
  1409.  
  1410.  
  1411. /*
  1412.  * ----------------------------------------------------------------------------
  1413.  *
  1414.  * Vm_PageIn --
  1415.  *
  1416.  *     This routine is called to read in the page at the given virtual address.
  1417.  *
  1418.  * Results:
  1419.  *     SUCCESS if the page-in was successful and FAILURE otherwise.
  1420.  *
  1421.  * Side effects:
  1422.  *     None.
  1423.  *
  1424.  * ----------------------------------------------------------------------------
  1425.  */
  1426. ReturnStatus
  1427. Vm_PageIn(virtAddr, protFault)
  1428.     Address     virtAddr;    /* The virtual address of the desired page */
  1429.     Boolean    protFault;    /* TRUE if fault is because of a protection
  1430.                  * violation. */
  1431. {
  1432.     register    Vm_PTE         *ptePtr;
  1433.     register    Vm_Segment    *segPtr;
  1434.     register    int        page;
  1435.     Vm_VirtAddr             transVirtAddr;    
  1436.     ReturnStatus         status;
  1437.     Proc_ControlBlock        *procPtr;
  1438.     unsigned    int        virtFrameNum;
  1439.     PrepareResult        result;
  1440.  
  1441.     vmStat.totalFaults++;
  1442.  
  1443.     procPtr = Proc_GetCurrentProc();
  1444.     /*
  1445.      * Determine which segment this virtual address falls into.
  1446.      */
  1447.     VmVirtAddrParse(procPtr, virtAddr, &transVirtAddr);
  1448.     segPtr = transVirtAddr.segPtr;
  1449.     page = transVirtAddr.page;
  1450.     if (segPtr == (Vm_Segment *) NIL) {
  1451.     return(FAILURE);
  1452.     }
  1453.     if (segPtr->flags & VM_SEG_IO_ERROR) {
  1454.     /*
  1455.      * Bad segment - disk full..  Go to pageinDone to clean up ptUserCount.
  1456.      * If a process is wildly growing its stack we'll have the heap locked
  1457.      * while we try to grow the stack, and we have to unlock the heap.
  1458.      */
  1459.     if (segPtr->type == VM_SHARED) {
  1460.         printf("Vm_PageIn: io error\n");
  1461.     }
  1462.     status = FAILURE;
  1463.     goto pageinDone;
  1464.     }
  1465.     if (protFault && ( segPtr->type == VM_CODE ||
  1466.         (transVirtAddr.flags & VM_READONLY_SEG))) {
  1467.     /*
  1468.      * Access violation.  Go to pageinDone to clean up ptUserCount.
  1469.      */
  1470.     if (segPtr->type == VM_SHARED) {
  1471.         printf("Vm_PageIn: access violation\n");
  1472.     }
  1473.     status = FAILURE;
  1474.     goto pageinDone;
  1475.     }
  1476.  
  1477.     /*
  1478.      * Make sure that the virtual address is within the allocated part of the
  1479.      * segment.  If not, then either return error if heap or code segment,
  1480.      * or automatically expand the stack if stack segment.
  1481.      */
  1482.     if (!VmCheckBounds(&transVirtAddr)) {
  1483.     if (segPtr->type == VM_STACK) {
  1484.         int    lastPage;
  1485.         /*
  1486.          * If this is a stack segment, then automatically grow it.
  1487.          */
  1488.         lastPage = mach_LastUserStackPage - segPtr->numPages;
  1489.         status = VmAddToSeg(segPtr, page, lastPage);
  1490.         if (status != SUCCESS) {
  1491.         goto pageinDone;
  1492.         }
  1493.     } else {
  1494.         if (segPtr->type == VM_SHARED) {
  1495.         dprintf("Vm_PageIn: VmCheckBounds failure\n");
  1496.         }
  1497.         status = FAILURE;
  1498.         goto pageinDone;
  1499.     }
  1500.     }
  1501.  
  1502.     switch (segPtr->type) {
  1503.     case VM_CODE:
  1504.         vmStat.codeFaults++;
  1505.         break;
  1506.     case VM_HEAP:
  1507.         vmStat.heapFaults++;
  1508.         break;
  1509.     case VM_STACK:
  1510.         vmStat.stackFaults++;
  1511.         break;
  1512.     }
  1513.  
  1514.     ptePtr = VmGetAddrPTEPtr(&transVirtAddr, page);
  1515.  
  1516.     if (protFault && (*ptePtr & VM_READ_ONLY_PROT) &&
  1517.         !(*ptePtr & VM_COR_CHECK_BIT)) {
  1518.     status = FAILURE;
  1519.     if (segPtr->type == VM_SHARED) {
  1520.         printf("Vm_PageIn: permission failure\n");
  1521.     }
  1522.     goto pageinDone;
  1523.     }
  1524.  
  1525.     /*
  1526.      * Fetch the next page.
  1527.      */
  1528.     if (vmPrefetch) {
  1529.     VmPrefetch(&transVirtAddr, ptePtr + 1);
  1530.     }
  1531.  
  1532.     while (TRUE) {
  1533.     /*
  1534.      * Do the first part of the page-in.
  1535.      */
  1536.     result = PreparePage(&transVirtAddr, protFault, ptePtr);
  1537.     if (!vm_CanCOW && (result == IS_COR || result == IS_COW)) {
  1538.         panic("Vm_PageIn: Bogus COW or COR\n");
  1539.     }
  1540.     if (result == IS_COR) {
  1541.         status = VmCOR(&transVirtAddr);
  1542.         if (status != SUCCESS) {
  1543.         if (segPtr->type == VM_SHARED) {
  1544.             printf("Vm_PageIn: VmCOR failure\n");
  1545.         }
  1546.         status = FAILURE;
  1547.         goto pageinError;
  1548.         }
  1549.     } else if (result == IS_COW) {
  1550.         VmCOW(&transVirtAddr);
  1551.     } else {
  1552.         break;
  1553.     }
  1554.     }
  1555.     if (result == IS_DONE) {
  1556.     if (transVirtAddr.segPtr->type == VM_SHARED) {
  1557.         dprintf("shared page at %x done early\n", virtAddr);
  1558.     }
  1559.     status = SUCCESS;
  1560.     goto pageinDone;
  1561.     }
  1562.  
  1563.     /*
  1564.      * Allocate a page.
  1565.      */
  1566.     virtFrameNum = VmPageAllocate(&transVirtAddr, TRUE);
  1567.     *ptePtr |= virtFrameNum;
  1568.  
  1569.     if (transVirtAddr.segPtr->type == VM_SHARED && debugVmStubs) {
  1570.     printf("paging in shared page to %x\n", virtAddr);
  1571.     }
  1572.  
  1573.     /*
  1574.      * Call the appropriate routine to fill the page.
  1575.      */
  1576.     if (*ptePtr & VM_ZERO_FILL_BIT) {
  1577.     vmStat.zeroFilled++;
  1578.     VmZeroPage(virtFrameNum);
  1579.     *ptePtr |= VM_MODIFIED_BIT;
  1580.     status = SUCCESS;
  1581.     } else if (*ptePtr & VM_ON_SWAP_BIT) {
  1582.     vmStat.psFilled++;
  1583.     if (transVirtAddr.segPtr->type == VM_SHARED) {
  1584.         dprintf("Vm_PageIn: paging in shared page %d\n",transVirtAddr.page);
  1585.     }
  1586.     status = VmPageServerRead(&transVirtAddr, virtFrameNum);
  1587.     } else {
  1588.     vmStat.fsFilled++;
  1589.     status = VmFileServerRead(&transVirtAddr, virtFrameNum);
  1590.     }
  1591.  
  1592.     *ptePtr |= VM_REFERENCED_BIT;
  1593.     if (vmWriteablePageout && transVirtAddr.segPtr->type != VM_CODE) {
  1594.     *ptePtr |= VM_MODIFIED_BIT;
  1595.     }
  1596.  
  1597.     /*
  1598.      * Finish up the page-in process.
  1599.      */
  1600.     FinishPage(&transVirtAddr, ptePtr);
  1601.  
  1602.     /*
  1603.      * Now check to see if the read succeeded.  If not destroy all processes
  1604.      * that are sharing the code segment.
  1605.      */
  1606. pageinError:
  1607.     if (status != SUCCESS) {
  1608.     if (transVirtAddr.segPtr->type == VM_SHARED) {
  1609.         printf("Vm_PageIn: Page read failed.  Invalidating pages.\n");
  1610.         VmPageFree(Vm_GetPageFrame(*ptePtr));
  1611.         VmPageInvalidateInt(&transVirtAddr, ptePtr);
  1612.     } else {
  1613.         VmKillSharers(segPtr);
  1614.     }
  1615.     }
  1616.  
  1617. pageinDone:
  1618.  
  1619.     if (transVirtAddr.flags & VM_HEAP_PT_IN_USE) {
  1620.     /*
  1621.      * The heap segment has been made not expandable by VmVirtAddrParse
  1622.      * so that the address parse would remain valid.  Decrement the
  1623.      * in use count now.
  1624.      */
  1625.     VmDecPTUserCount(procPtr->vmPtr->segPtrArray[VM_HEAP]);
  1626.     }
  1627.  
  1628.     return(status);
  1629. }
  1630.  
  1631.  
  1632. /*
  1633.  * ----------------------------------------------------------------------------
  1634.  *
  1635.  * PreparePage --
  1636.  *
  1637.  *    This routine performs the first half of the page-in process.
  1638.  *    It will return a status to the caller telling them what the status
  1639.  *    of the page is.
  1640.  *
  1641.  * Results:
  1642.  *    IS_DONE if the page is already resident in memory and it is not a 
  1643.  *    COW faults.  IS_COR is it is for a copy-on-reference fault.  IS_COW
  1644.  *    if is for a copy-on-write fault.  Otherwise returns NOT_DONE.
  1645.  *
  1646.  * Side effects:
  1647.  *    *ptePtrPtr is set to point to the page table entry for this virtual
  1648.  *    page.  In progress bit set if the NOT_DONE status is returned.
  1649.  *
  1650.  * ----------------------------------------------------------------------------
  1651.  */
  1652. ENTRY static PrepareResult
  1653. PreparePage(virtAddrPtr, protFault, curPTEPtr)
  1654.     register Vm_VirtAddr *virtAddrPtr;     /* The translated virtual address */
  1655.     Boolean        protFault;    /* TRUE if faulted because of a
  1656.                      * protection fault. */
  1657.     register    Vm_PTE    *curPTEPtr;    /* Page table pointer for the page. */
  1658. {
  1659.     PrepareResult    retVal;
  1660.  
  1661.     LOCK_MONITOR;
  1662.  
  1663. again:
  1664.     if (*curPTEPtr & VM_IN_PROGRESS_BIT) {
  1665.     /*
  1666.      * The page is being faulted on by someone else.  In this case wait
  1667.      * for the page fault to complete.
  1668.      */
  1669.     vmStat.collFaults++;
  1670.     (void) Sync_Wait(&virtAddrPtr->segPtr->condition, FALSE);
  1671.     goto again;
  1672.     } else if (*curPTEPtr & VM_COR_BIT) {
  1673.     /*
  1674.      * Copy-on-reference fault.
  1675.      */
  1676.     retVal = IS_COR;
  1677.     } else if (protFault && (*curPTEPtr & VM_COW_BIT) && 
  1678.            (*curPTEPtr & VM_PHYS_RES_BIT)) {
  1679.     /*
  1680.      * Copy-on-write fault.
  1681.      */
  1682.     retVal = IS_COW;
  1683.     } else if (*curPTEPtr & VM_PHYS_RES_BIT) {
  1684.     /*
  1685.      * The page is already in memory.  Validate it in hardware and set
  1686.      * the reference bit since we are about to reference it.
  1687.      */
  1688.     if (protFault && (*curPTEPtr & VM_COR_CHECK_BIT)) {
  1689.         if (virtAddrPtr->segPtr->type == VM_HEAP) {
  1690.         vmStat.numCORCOWHeapFaults++;
  1691.         } else {
  1692.         vmStat.numCORCOWStkFaults++;
  1693.         }
  1694.         *curPTEPtr &= ~(VM_COR_CHECK_BIT | VM_READ_ONLY_PROT);
  1695.     } else {
  1696.         /* 
  1697.          * Remove "quick" faults from the per-segment counts, so 
  1698.          * that the per-segment counts are more meaningful.
  1699.          */
  1700.         vmStat.quickFaults++;
  1701.         switch (virtAddrPtr->segPtr->type) {
  1702.         case VM_CODE:
  1703.         vmStat.codeFaults--;
  1704.         break;
  1705.         case VM_HEAP:
  1706.         vmStat.heapFaults--;
  1707.         break;
  1708.         case VM_STACK:
  1709.         vmStat.stackFaults--;
  1710.         break;
  1711.         }
  1712.     }
  1713.     if (*curPTEPtr & VM_PREFETCH_BIT) {
  1714.         switch (virtAddrPtr->segPtr->type) {
  1715.         case VM_CODE:
  1716.             vmStat.codePrefetchHits++;
  1717.             break;
  1718.         case VM_HEAP:
  1719.             if (*curPTEPtr & VM_ON_SWAP_BIT) {
  1720.             vmStat.heapSwapPrefetchHits++;
  1721.             } else {
  1722.             vmStat.heapFSPrefetchHits++;
  1723.             }
  1724.             break;
  1725.         case VM_STACK:
  1726.             vmStat.stackPrefetchHits++;
  1727.             break;
  1728.         }
  1729.         *curPTEPtr &= ~VM_PREFETCH_BIT;
  1730.     }
  1731.     VmPageValidateInt(virtAddrPtr, curPTEPtr);
  1732.     *curPTEPtr |= VM_REFERENCED_BIT;
  1733.         retVal = IS_DONE;
  1734.     } else {
  1735.     *curPTEPtr |= VM_IN_PROGRESS_BIT;
  1736.     retVal = NOT_DONE;
  1737.     }
  1738.  
  1739.     UNLOCK_MONITOR;
  1740.     return(retVal);
  1741. }
  1742.  
  1743.  
  1744. /*
  1745.  * ----------------------------------------------------------------------------
  1746.  *
  1747.  * FinishPage --
  1748.  *    This routine finishes the page-in process.  This includes validating
  1749.  *    the page for the currently executing process and releasing the 
  1750.  *    lock on the page.
  1751.  *
  1752.  * Results:
  1753.  *    None.
  1754.  *
  1755.  * Side effects:
  1756.  *    Page-in in progress cleared and lockcount decremented in the
  1757.  *     core map entry.
  1758.  *    
  1759.  *
  1760.  * ----------------------------------------------------------------------------
  1761.  */
  1762. ENTRY static void
  1763. FinishPage(transVirtAddrPtr, ptePtr) 
  1764.     register    Vm_VirtAddr    *transVirtAddrPtr;
  1765.     register    Vm_PTE        *ptePtr;
  1766. {
  1767.     LOCK_MONITOR;
  1768.  
  1769.     /*
  1770.      * Make the page accessible to the user.
  1771.      */
  1772.     VmPageValidateInt(transVirtAddrPtr, ptePtr);
  1773.     coreMap[Vm_GetPageFrame(*ptePtr)].lockCount--;
  1774.     *ptePtr &= ~(VM_ZERO_FILL_BIT | VM_IN_PROGRESS_BIT);
  1775.     /*
  1776.      * Wakeup processes waiting for this pagein to complete.
  1777.      */
  1778.     Sync_Broadcast(&transVirtAddrPtr->segPtr->condition);
  1779.  
  1780.     UNLOCK_MONITOR;
  1781. }
  1782.  
  1783.  
  1784. /*
  1785.  *----------------------------------------------------------------------
  1786.  *
  1787.  * KillCallback --
  1788.  *
  1789.  *    Send a process the SIG_KILL signal when an I/O error
  1790.  *    occurs.  This routine is a callback procedure used by
  1791.  *    VmKillSharers to perform signals without the vm monitor lock
  1792.  *    held.
  1793.  *
  1794.  * Results:
  1795.  *    None.
  1796.  *
  1797.  * Side effects:
  1798.  *    The specified process is killed.
  1799.  *
  1800.  *----------------------------------------------------------------------
  1801.  */
  1802.  
  1803. static void
  1804. KillCallback(data)
  1805.     ClientData data;
  1806. {
  1807.     (void) Sig_Send(SIG_KILL, PROC_VM_READ_ERROR, (Proc_PID) data, FALSE,
  1808.         (Address)0);
  1809. }
  1810.  
  1811. /*
  1812.  * ----------------------------------------------------------------------------
  1813.  *
  1814.  * VmKillSharers --
  1815.  *
  1816.  *    Go down the list of processes sharing this segment and set up
  1817.  *    a callback to send a kill signal to each one without the
  1818.  *    monitor lock held.  This is called when a page from a segment
  1819.  *    couldn't be written to or read from swap space.
  1820.  *
  1821.  * Results:
  1822.  *     None.
  1823.  *
  1824.  * Side effects:
  1825.  *     All processes sharing this segment are destroyed.
  1826.  *    Marks the segment as having an I/O error.
  1827.  *
  1828.  * ----------------------------------------------------------------------------
  1829.  */
  1830.  
  1831. ENTRY void
  1832. VmKillSharers(segPtr) 
  1833.     register    Vm_Segment    *segPtr;
  1834. {
  1835.     register    VmProcLink    *procLinkPtr;
  1836.  
  1837.     LOCK_MONITOR;
  1838.  
  1839.     if ((segPtr->flags & VM_SEG_IO_ERROR) == 0) {
  1840.     LIST_FORALL(segPtr->procList, (List_Links *) procLinkPtr) {
  1841.         Proc_CallFunc(KillCallback,
  1842.               (ClientData) procLinkPtr->procPtr->processID,
  1843.               0);
  1844.     }
  1845.     }
  1846.     segPtr->flags |= VM_SEG_IO_ERROR;
  1847.  
  1848.     UNLOCK_MONITOR;
  1849. }
  1850.  
  1851. static void PinPages _ARGS_((register Vm_VirtAddr *virtAddrPtr, register int lastPage));
  1852.  
  1853.  
  1854. /*
  1855.  * ----------------------------------------------------------------------------
  1856.  *
  1857.  * Vm_PinUserMem --
  1858.  *
  1859.  *      Hardwire pages for all user addresses between firstAddr and
  1860.  *    lastAddr.
  1861.  *
  1862.  * Results:
  1863.  *     SUCCESS if the page-in was successful and SYS_ARG_NO_ACCESS otherwise.
  1864.  *
  1865.  * Side effects:
  1866.  *     Pages between firstAddr and lastAddr are wired down in memory.
  1867.  *
  1868.  * ----------------------------------------------------------------------------
  1869.  */
  1870. ReturnStatus
  1871. Vm_PinUserMem(mapType, numBytes, addr)
  1872.     int             mapType;    /* VM_READONLY_ACCESS | VM_READWRITE_ACCESS */
  1873.     int             numBytes;    /* Number of bytes to map. */
  1874.     register Address addr;    /* Where to start mapping at. */
  1875. {
  1876.     Vm_VirtAddr             virtAddr;
  1877.     ReturnStatus        status = SUCCESS;
  1878.     int                firstPage;
  1879.     int                lastPage;
  1880.     Proc_ControlBlock        *procPtr;
  1881.  
  1882.     procPtr = Proc_GetCurrentProc();
  1883.     VmVirtAddrParse(procPtr, addr, &virtAddr);
  1884.     if (virtAddr.segPtr == (Vm_Segment *)NIL ||
  1885.     (virtAddr.segPtr->type == VM_CODE && mapType == VM_READWRITE_ACCESS)) {
  1886.     return(SYS_ARG_NOACCESS);
  1887.     }
  1888.  
  1889.     firstPage = virtAddr.page;
  1890.     lastPage = ((unsigned)addr + numBytes - 1) >> vmPageShift;
  1891.     while (virtAddr.page <= lastPage) {
  1892.     /*
  1893.      * Loop until we got all of the pages locked down.  We have to
  1894.      * loop because a page could get freed after we touch it but before
  1895.      * we get a chance to wire it down.
  1896.      */
  1897.     status = Vm_TouchPages(virtAddr.page, lastPage - virtAddr.page + 1);
  1898.     if (status != SUCCESS) {
  1899.         goto done;
  1900.     }
  1901.     PinPages(&virtAddr, lastPage);
  1902.     }
  1903.  
  1904.     virtAddr.page = firstPage;
  1905.     VmMach_PinUserPages(mapType,  &virtAddr, lastPage);
  1906.  
  1907. done:
  1908.     if (virtAddr.flags & VM_HEAP_PT_IN_USE) {
  1909.     VmDecPTUserCount(procPtr->vmPtr->segPtrArray[VM_HEAP]);
  1910.     }
  1911.     return(status);
  1912. }
  1913.  
  1914.  
  1915. /*
  1916.  * ----------------------------------------------------------------------------
  1917.  *
  1918.  * PinPages --
  1919.  *
  1920.  *      Hardwire pages for all user addresses between firstAddr and
  1921.  *    lastAddr.
  1922.  *
  1923.  * Results:
  1924.  *      None.
  1925.  *
  1926.  * Side effects:
  1927.  *    virtAddrPtr->page is updated as we successfully wire down pages.  When
  1928.  *    we return its value will be of the last page that we successfully
  1929.  *    wired down + 1.
  1930.  *
  1931.  * ----------------------------------------------------------------------------
  1932.  */
  1933. static void
  1934. PinPages(virtAddrPtr, lastPage)
  1935.     register    Vm_VirtAddr    *virtAddrPtr;
  1936.     register    int        lastPage;
  1937. {
  1938.     register    VmCore    *corePtr;
  1939.     register    Vm_PTE    *ptePtr;
  1940.  
  1941.     LOCK_MONITOR;
  1942.  
  1943.     for (ptePtr = VmGetAddrPTEPtr(virtAddrPtr, virtAddrPtr->page);
  1944.          virtAddrPtr->page <= lastPage;
  1945.      VmIncPTEPtr(ptePtr, 1), virtAddrPtr->page++) {
  1946.     while (*ptePtr & VM_IN_PROGRESS_BIT) {
  1947.         (void)Sync_Wait(&virtAddrPtr->segPtr->condition, FALSE);
  1948.     }
  1949.     if (*ptePtr & VM_PHYS_RES_BIT) {
  1950.         corePtr = &coreMap[Vm_GetPageFrame(*ptePtr)];
  1951.         corePtr->wireCount++;
  1952.         corePtr->lockCount++;
  1953.     } else {
  1954.         break;
  1955.     }
  1956.     }
  1957.  
  1958.     UNLOCK_MONITOR;
  1959. }
  1960.  
  1961. static void UnpinPages _ARGS_((Vm_VirtAddr *virtAddrPtr, int lastPage));
  1962.  
  1963.  
  1964. /*
  1965.  * ----------------------------------------------------------------------------
  1966.  *
  1967.  * Vm_UnpinUserMem --
  1968.  *
  1969.  *      Unlock all pages between firstAddr and lastAddr.
  1970.  *    lastAddr.
  1971.  *
  1972.  * Results:
  1973.  *     SUCCESS if the page-in was successful and FAILURE otherwise.
  1974.  *
  1975.  * Side effects:
  1976.  *     None.
  1977.  *
  1978.  * ----------------------------------------------------------------------------
  1979.  */
  1980. void
  1981. Vm_UnpinUserMem(numBytes, addr)
  1982.     int        numBytes;    /* The number of bytes to map. */
  1983.     Address     addr;        /* The address to start mapping at. */
  1984. {
  1985.     Vm_VirtAddr             virtAddr;
  1986.     Proc_ControlBlock        *procPtr;
  1987.     int                lastPage;
  1988.  
  1989.     procPtr = Proc_GetCurrentProc();
  1990.     VmVirtAddrParse(procPtr, addr, &virtAddr);
  1991.     lastPage = (unsigned int)(addr + numBytes - 1) >> vmPageShift;
  1992.     /*
  1993.      * Now unlock all of the pages.
  1994.      */
  1995.     VmMach_UnpinUserPages(&virtAddr, lastPage);
  1996.     UnpinPages(&virtAddr, lastPage);
  1997.  
  1998.     if (virtAddr.flags & VM_HEAP_PT_IN_USE) {
  1999.     /*
  2000.      * The heap segment has been made not expandable by VmVirtAddrParse
  2001.      * so that the address parse would remain valid.  Decrement the
  2002.      * in use count now.
  2003.      */
  2004.     VmDecPTUserCount(procPtr->vmPtr->segPtrArray[VM_HEAP]);
  2005.     }
  2006. }
  2007.  
  2008.  
  2009. /*
  2010.  * ----------------------------------------------------------------------------
  2011.  *
  2012.  * UnpinPages --
  2013.  *
  2014.  *      Unlock pages for all user addresses between firstAddr and
  2015.  *    lastAddr.
  2016.  *
  2017.  * Results:
  2018.  *    None
  2019.  *
  2020.  * Side effects:
  2021.  *    Core map entry lock count and flags field may be modified.
  2022.  *
  2023.  * ----------------------------------------------------------------------------
  2024.  */
  2025. static void
  2026. UnpinPages(virtAddrPtr, lastPage)
  2027.     Vm_VirtAddr    *virtAddrPtr;
  2028.     int        lastPage;
  2029. {
  2030.     register    VmCore    *corePtr;
  2031.     register    Vm_PTE    *ptePtr;
  2032.     register    int    i;
  2033.  
  2034.     LOCK_MONITOR;
  2035.  
  2036.     for (i = virtAddrPtr->page,
  2037.         ptePtr = VmGetAddrPTEPtr(virtAddrPtr, virtAddrPtr->page);
  2038.      i <= lastPage;
  2039.      VmIncPTEPtr(ptePtr, 1), i++) {
  2040.  
  2041.     while (*ptePtr & VM_IN_PROGRESS_BIT) {
  2042.         (void)Sync_Wait(&virtAddrPtr->segPtr->condition, FALSE);
  2043.     }
  2044.  
  2045.     if (*ptePtr & VM_PHYS_RES_BIT) {
  2046.         corePtr = &coreMap[Vm_GetPageFrame(*ptePtr)];
  2047.         if (corePtr->wireCount > 0) {
  2048.         corePtr->wireCount--;
  2049.         corePtr->lockCount--;
  2050.         }
  2051.     }
  2052.     }
  2053.  
  2054.     UNLOCK_MONITOR;
  2055. }
  2056.  
  2057.  
  2058.  
  2059. /*
  2060.  * ----------------------------------------------------------------------------
  2061.  *
  2062.  * VmPagePinned --
  2063.  *
  2064.  *      Return TRUE if the page is wired down in memory and FALSE otherwise.
  2065.  *
  2066.  * Results:
  2067.  *     TRUE if the page is wired down and FALSE otherwise.
  2068.  *
  2069.  * Side effects:
  2070.  *     None.
  2071.  *
  2072.  * ----------------------------------------------------------------------------
  2073.  */
  2074. INTERNAL Boolean
  2075. VmPagePinned(ptePtr)
  2076.     Vm_PTE    *ptePtr;
  2077. {
  2078.     return(coreMap[Vm_GetPageFrame(*ptePtr)].wireCount > 0);
  2079. }
  2080.  
  2081.  
  2082. /*----------------------------------------------------------------------------
  2083.  *
  2084.  *         Routines for writing out dirty pages        
  2085.  *
  2086.  * Dirty pages are written to the swap file by the function PageOut.
  2087.  * PageOut is called by using the Proc_CallFunc routine which invokes
  2088.  * a process on PageOut.  When a page is put onto the dirty list a new
  2089.  * incantation of PageOut will be created unless there are already
  2090.  * more than vmMaxPageOutProcs already writing out the dirty list.  Thus the
  2091.  * dirty list will be cleaned by at most vmMaxPageOutProcs working in parallel.
  2092.  *
  2093.  * The work done by PageOut is split into work done at non-monitor level and
  2094.  * monitor level.  It calls the monitored routine PageOutPutAndGet to get the 
  2095.  * next page off of the dirty list.  It then writes the page out to the 
  2096.  * file server at non-monitor level.  Next it calls the monitored routine 
  2097.  * PageOutPutAndGet to put the page onto the front of the allocate list and
  2098.  * get the next dirty page.  Finally when there are no more pages to clean it
  2099.  * returns (and dies).
  2100.  */
  2101.  
  2102. static void PageOutPutAndGet _ARGS_((VmCore **corePtrPtr, ReturnStatus status, Boolean *doRecoveryPtr, Fs_Stream **recStreamPtrPtr));
  2103. static void PutOnFront _ARGS_((register VmCore *corePtr));
  2104.  
  2105. /*
  2106.  * ----------------------------------------------------------------------------
  2107.  *
  2108.  * PageOut --
  2109.  *
  2110.  *    Function to write out pages on dirty list.  It will keep retrieving
  2111.  *    pages from the dirty list until there are no more left.  This function
  2112.  *    is designed to be called through Proc_CallFunc.
  2113.  *    
  2114.  * Results:
  2115.  *         None.
  2116.  *
  2117.  * Side effects:
  2118.  *         The dirty list is emptied.
  2119.  *
  2120.  * ----------------------------------------------------------------------------
  2121.  */
  2122. /* ARGSUSED */
  2123. static void
  2124. PageOut(data, callInfoPtr)
  2125.     ClientData        data;        /* Ignored. */
  2126.     Proc_CallInfo    *callInfoPtr;    /* Ignored. */
  2127. {
  2128.     VmCore        *corePtr;
  2129.     ReturnStatus    status = SUCCESS;
  2130.     Fs_Stream        *recoveryStreamPtr;
  2131.     Boolean        doRecovery;
  2132.     Boolean        returnSwapStream;
  2133.  
  2134.     vmStat.pageoutWakeup++;
  2135.  
  2136.     corePtr = (VmCore *) NIL;
  2137.     while (TRUE) {
  2138.     doRecovery = FALSE;
  2139.     PageOutPutAndGet(&corePtr, status, &doRecovery, &recoveryStreamPtr);
  2140.     if (doRecovery) {
  2141.         /*
  2142.          * The following shenanigans are used to carefully
  2143.          * synchronize access to the swap directory stream.
  2144.          */
  2145.         returnSwapStream = FALSE;
  2146.         if (recoveryStreamPtr == (Fs_Stream  *)NIL) {
  2147.         recoveryStreamPtr = VmGetSwapStreamPtr();
  2148.         if (recoveryStreamPtr != (Fs_Stream *)NIL) {
  2149.             returnSwapStream = TRUE;
  2150.         }
  2151.         }
  2152.         if (recoveryStreamPtr != (Fs_Stream  *)NIL) {
  2153.         (void)Fsutil_WaitForHost(recoveryStreamPtr, FS_NON_BLOCKING,
  2154.                      status);
  2155.         if (returnSwapStream) {
  2156.             VmDoneWithSwapStreamPtr();
  2157.         }
  2158.         }
  2159.     }
  2160.  
  2161.     if (corePtr == (VmCore *) NIL) {
  2162.         break;
  2163.     }
  2164.     status = VmPageServerWrite(&corePtr->virtPage, 
  2165.                    (unsigned int) (corePtr - coreMap),
  2166.                    FALSE);
  2167.     if (status != SUCCESS) {
  2168.         if ( ! VmSwapStreamOk() ||
  2169.             (status != RPC_TIMEOUT && status != FS_STALE_HANDLE &&
  2170.          status != RPC_SERVICE_DISABLED)) {
  2171.         /*
  2172.          * Non-recoverable error on page write, so kill all users of 
  2173.          * this segment.
  2174.          */
  2175.         VmKillSharers(corePtr->virtPage.segPtr);
  2176.         }
  2177.     }
  2178.     }
  2179.  
  2180. }
  2181.  
  2182.  
  2183. /*
  2184.  * ----------------------------------------------------------------------------
  2185.  *
  2186.  * PageOutPutAndGet --
  2187.  *
  2188.  *    This routine does two things.  First it puts the page pointed to by
  2189.  *    *corePtrPtr (if any) onto the front of the allocate list and wakes
  2190.  *    up any dying processes waiting for this page to be cleaned.
  2191.  *    It then takes the first page off of the dirty list and returns a 
  2192.  *    pointer to it.  Before returning the pointer it clears the 
  2193.  *      modified bit of the page frame.
  2194.  *
  2195.  * Results:
  2196.  *     A pointer to the first page on the dirty list.  If there are no pages
  2197.  *     then *corePtrPtr is set to NIL.
  2198.  *
  2199.  * Side effects:
  2200.  *    The dirty list and allocate lists may both be modified.  In addition
  2201.  *      the onSwap bit is set to indicate that the page is now on swap space.
  2202.  *
  2203.  * ----------------------------------------------------------------------------
  2204.  */
  2205. ENTRY static void
  2206. PageOutPutAndGet(corePtrPtr, status, doRecoveryPtr, recStreamPtrPtr)
  2207.     VmCore     **corePtrPtr;        /* On input points to page frame
  2208.                      * to be put back onto allocate list.
  2209.                      * On output points to page frame
  2210.                      * to be cleaned. */
  2211.     ReturnStatus status;        /* Status from the write. */
  2212.     Boolean    *doRecoveryPtr;        /* Return.  TRUE if recovery should
  2213.                      * be attempted.  In this case check
  2214.                      * *recStreamPtrPtr and wait for
  2215.                      * recovery on that, or wait for
  2216.                      * recovery on vmSwapStreamPtr. */
  2217.     Fs_Stream     **recStreamPtrPtr;    /* Pointer to stream to do recovery
  2218.                      * on if *doRecoveryPtr is TRUE.  If
  2219.                      * this is still NIL, then do recovery
  2220.                      * on vmSwapStreamPtr instead */
  2221. {
  2222.     register    Vm_PTE    *ptePtr;
  2223.     register    VmCore    *corePtr;
  2224.  
  2225.     LOCK_MONITOR;
  2226.  
  2227.     *doRecoveryPtr = FALSE;
  2228.     *recStreamPtrPtr = (Fs_Stream *)NIL;
  2229.     corePtr = *corePtrPtr;
  2230.     if (corePtr == (VmCore *)NIL) {
  2231.     if (swapDown) {
  2232.         numPageOutProcs--;
  2233.         UNLOCK_MONITOR;
  2234.         return;
  2235.     }
  2236.     } else {
  2237.     switch (status) {
  2238.         case RPC_TIMEOUT:
  2239.         case RPC_SERVICE_DISABLED:
  2240.         case FS_STALE_HANDLE: {
  2241.             if (!swapDown) {
  2242.             /*
  2243.              * We have not realized that we have an error yet.
  2244.              * Mark the swap server as down, and return a
  2245.              * pointer to the swap stream.  If it isn't open
  2246.              * yet we'll return NIL, and our caller should use
  2247.              * vmSwapStreamPtr for recovery, which is guarded
  2248.              * by a different monitor.
  2249.              */
  2250.             *recStreamPtrPtr = corePtr->virtPage.segPtr->swapFilePtr;
  2251.             *doRecoveryPtr = TRUE;
  2252.             swapDown = TRUE;
  2253.             }
  2254.             corePtr->flags &= ~VM_PAGE_BEING_CLEANED;
  2255.             VmListInsert((List_Links *)corePtr,
  2256.                  LIST_ATREAR(dirtyPageList));
  2257.             *corePtrPtr = (VmCore *)NIL;
  2258.             numPageOutProcs--;
  2259.             UNLOCK_MONITOR;
  2260.             return;
  2261.         }
  2262.         break;
  2263.         default:
  2264.         break;
  2265.     }
  2266.     PutOnFront(corePtr);
  2267.     corePtr = (VmCore *) NIL;    
  2268.     }
  2269.  
  2270.     while (!List_IsEmpty(dirtyPageList)) {
  2271.         /*
  2272.      * Get the first page off of the dirty list.
  2273.      */
  2274.     corePtr = (VmCore *) List_First(dirtyPageList);
  2275.     VmListRemove((List_Links *) corePtr);
  2276.     /*
  2277.      * If this segment is being deleted then invalidate the page and
  2278.      * then free it.
  2279.      */
  2280.         if (corePtr->virtPage.segPtr->flags & VM_SEG_DEAD) {
  2281.         vmStat.numDirtyPages--;
  2282.         VmPageInvalidateInt(&corePtr->virtPage,
  2283.         VmGetAddrPTEPtr(&corePtr->virtPage, corePtr->virtPage.page));
  2284.         PutOnFreeList(corePtr);
  2285.         corePtr = (VmCore *) NIL;
  2286.     } else {
  2287.         break;
  2288.     }
  2289.     }
  2290.  
  2291.     if (corePtr != (VmCore *) NIL) {
  2292.     /*
  2293.      * This page will now be on the page server so set the pte accordingly.
  2294.      * In addition the modified bit must be cleared here since the page
  2295.      * could get modified while it is being cleaned.
  2296.      */
  2297.     ptePtr = VmGetAddrPTEPtr(&corePtr->virtPage, corePtr->virtPage.page);
  2298.     *ptePtr |= VM_ON_SWAP_BIT;
  2299.     /*
  2300.      * If the page has become locked while it was on the dirty list, don't
  2301.      * clear the modify bit.  The set modify bit after the page write 
  2302.      * completes will cause this page to be put back on the alloc list.
  2303.      */
  2304.     if (corePtr->lockCount == 0) {
  2305.         *ptePtr &= ~VM_MODIFIED_BIT;
  2306.         VmMach_ClearModBit(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr));
  2307.     }
  2308.     corePtr->flags |= VM_PAGE_BEING_CLEANED;
  2309.     } else {
  2310.     /*
  2311.      * No dirty pages.  Decrement the number of page out procs and
  2312.      * return nil.  PageOut will kill itself when it receives NIL.
  2313.      */
  2314.     numPageOutProcs--;
  2315.  
  2316.     if (numPageOutProcs == 0 && vmStat.numDirtyPages != 0) {
  2317.         panic("PageOutPutAndGet: Dirty pages but no pageout procs\n");
  2318.     }
  2319.     }
  2320.  
  2321.     *corePtrPtr = corePtr;
  2322.  
  2323.     UNLOCK_MONITOR;
  2324. }
  2325.  
  2326.  
  2327. /*
  2328.  * ----------------------------------------------------------------------------
  2329.  *
  2330.  * PutOnFront --
  2331.  *
  2332.  *    Take one of two actions.  If page frame is already marked as free
  2333.  *    then put it onto the front of the free list.  Otherwise put it onto
  2334.  *    the front of the allocate list.  
  2335.  *
  2336.  * Results:
  2337.  *    None.    
  2338.  *
  2339.  * Side effects:
  2340.  *    Allocate list or free list modified.
  2341.  *
  2342.  * ----------------------------------------------------------------------------
  2343.  */
  2344. INTERNAL static void
  2345. PutOnFront(corePtr)
  2346.     register    VmCore    *corePtr;
  2347. {
  2348.     register    Vm_PTE    *ptePtr;
  2349.  
  2350.     if (corePtr->flags & VM_SEG_PAGEOUT_WAIT) {
  2351.     Sync_Broadcast(&corePtr->virtPage.segPtr->condition);
  2352.     }
  2353.     corePtr->flags &= ~(VM_DIRTY_PAGE | VM_PAGE_BEING_CLEANED | 
  2354.                 VM_SEG_PAGEOUT_WAIT | VM_DONT_FREE_UNTIL_CLEAN);
  2355.     if (corePtr->flags & VM_FREE_PAGE) {
  2356.     PutOnFreeList(corePtr);
  2357.     } else {
  2358.     Boolean    referenced, modified;
  2359.     /*
  2360.      * Update the software page table from the hardware.  This 
  2361.      * catches the case when a page that we are writting out is
  2362.      * modified.  
  2363.      */
  2364.     ptePtr = VmGetAddrPTEPtr(&corePtr->virtPage,
  2365.                  corePtr->virtPage.page);
  2366.     referenced = *ptePtr & VM_REFERENCED_BIT;
  2367.     modified = *ptePtr & VM_MODIFIED_BIT;
  2368.     VmMach_GetRefModBits(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr),
  2369.               &referenced, &modified);
  2370.     if (referenced) {
  2371.         *ptePtr |= (VM_REFERENCED_BIT);
  2372.     }
  2373.     if (modified) {
  2374.         *ptePtr |= (VM_REFERENCED_BIT|VM_MODIFIED_BIT);
  2375.     }
  2376.     if (vmFreeWhenClean && corePtr->lockCount == 0 && 
  2377.                     !(*ptePtr & VM_REFERENCED_BIT)) {
  2378.         /*
  2379.          * We are supposed to free pages after we clean them.  Before
  2380.          * we put this page onto the dirty list, we already invalidated
  2381.          * it in hardware, thus forcing it to be faulted on before being
  2382.          * referenced.  If it was faulted on then PreparePage would have
  2383.          * set the reference bit in the PTE.  Thus if the reference bit
  2384.          * isn't set then the page isn't valid and thus it couldn't
  2385.          * possibly have been modified or referenced.  So we free this
  2386.          * page.
  2387.          */
  2388.          if (!(*ptePtr & VM_PHYS_RES_BIT)) {
  2389.            panic("PutOnFront: Resident bit not set\n");
  2390.          }
  2391.          corePtr->virtPage.segPtr->resPages--;
  2392.          *ptePtr &= ~(VM_PHYS_RES_BIT | VM_PAGE_FRAME_FIELD);
  2393.         PutOnFreeList(corePtr);
  2394.     } else {
  2395.         PutOnAllocListFront(corePtr);
  2396.     }
  2397.     }
  2398.     vmStat.numDirtyPages--; 
  2399.     Sync_Broadcast(&cleanCondition);
  2400. }
  2401.  
  2402.  
  2403. /*
  2404.  * Variables for the clock daemon.  vmPagesToCheck is the number of page 
  2405.  * frames to examine each time that the clock daemon wakes up.  vmClockSleep
  2406.  * is the amount of time for the clock daemon before it runs again.
  2407.  */
  2408. unsigned int    vmClockSleep;        
  2409. int        vmPagesToCheck = 100;
  2410. static    int    clockHand = 0;
  2411.  
  2412. /*
  2413.  * ----------------------------------------------------------------------------
  2414.  *
  2415.  * Vm_Clock --
  2416.  *
  2417.  *    Main loop for the clock daemon process.  It will wakeup every 
  2418.  *    few seconds, examine a few page frames, and then go back to sleep.
  2419.  *    It is used to keep the allocate list in approximate LRU order.
  2420.  *
  2421.  * Results:
  2422.  *         None.
  2423.  *
  2424.  * Side effects:
  2425.  *         The allocate list is modified.
  2426.  *
  2427.  * ----------------------------------------------------------------------------
  2428.  */
  2429. /*ARGSUSED*/
  2430. ENTRY void
  2431. Vm_Clock(data, callInfoPtr)
  2432.     ClientData    data;
  2433.     Proc_CallInfo    *callInfoPtr;
  2434. {
  2435.     static Boolean initialized = FALSE;
  2436.  
  2437.     register    VmCore    *corePtr;
  2438.     register    Vm_PTE    *ptePtr;
  2439.     int            i;
  2440.     Time        curTime;
  2441.     Boolean        referenced;
  2442.     Boolean        modified;
  2443.  
  2444.     LOCK_MONITOR;
  2445.  
  2446.     Timer_GetTimeOfDay(&curTime, (int *) NIL, (Boolean *) NIL);
  2447.  
  2448.     /*
  2449.      * Examine vmPagesToCheck pages.
  2450.      */
  2451.     for (i = 0; i < vmPagesToCheck; i++) {
  2452.     corePtr = &(coreMap[clockHand]);
  2453.  
  2454.     /*
  2455.      * Move to the next page in the core map.  If have reached the
  2456.      * end of the core map then go back to the first page that may not
  2457.      * be used by the kernel.
  2458.      */
  2459.     if (clockHand == vmStat.numPhysPages - 1) {
  2460.         clockHand = vmFirstFreePage;
  2461.     } else {
  2462.         clockHand++;
  2463.     }
  2464.  
  2465.     /*
  2466.      * If the page is free, locked, in the middle of a page-in, 
  2467.      * or in the middle of a pageout, then we aren't concerned 
  2468.      * with this page.
  2469.      */
  2470.     if ((corePtr->flags & (VM_DIRTY_PAGE | VM_FREE_PAGE)) ||
  2471.         corePtr->lockCount > 0) {
  2472.         continue;
  2473.     }
  2474.  
  2475.     ptePtr = VmGetPTEPtr(corePtr->virtPage.segPtr, corePtr->virtPage.page);
  2476.     /*
  2477.      * If the page has been referenced, then put it on the end of the
  2478.      * allocate list.
  2479.      */
  2480.     VmMach_GetRefModBits(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr),
  2481.                  &referenced, &modified);
  2482.     if ((*ptePtr & VM_REFERENCED_BIT) || referenced) {
  2483.         VmListMove((List_Links *) corePtr, LIST_ATREAR(allocPageList));
  2484.         corePtr->lastRef = curTime.seconds;
  2485.         *ptePtr &= ~VM_REFERENCED_BIT;
  2486.         VmMach_ClearRefBit(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr));
  2487.         if (vmWriteableRefPageout &&
  2488.         corePtr->virtPage.segPtr->type != VM_CODE) {
  2489.         *ptePtr |= VM_MODIFIED_BIT;
  2490.         }
  2491.     }
  2492.     }
  2493.     if (!initialized) {
  2494.         vmClockSleep = timer_IntOneSecond;
  2495.     initialized = TRUE;
  2496.     }
  2497.     callInfoPtr->interval = vmClockSleep;
  2498.     UNLOCK_MONITOR;
  2499.     return;
  2500. }
  2501.  
  2502. /*
  2503.  * ----------------------------------------------------------------------------
  2504.  *
  2505.  * VmCountDirtyPages --
  2506.  *
  2507.  *    Return the number of dirty pages.
  2508.  *
  2509.  * Results:
  2510.  *         None.
  2511.  *
  2512.  * Side effects:
  2513.  *         The allocate list is modified.
  2514.  *
  2515.  * ----------------------------------------------------------------------------
  2516.  */
  2517. ENTRY int
  2518. VmCountDirtyPages()
  2519. {
  2520.     register    Vm_PTE    *ptePtr;
  2521.     register    VmCore    *corePtr;
  2522.     register    int    i;
  2523.     register    int    numDirtyPages = 0;
  2524.     Boolean        referenced;
  2525.     Boolean        modified;
  2526.  
  2527.     LOCK_MONITOR;
  2528.  
  2529.     for (corePtr = &coreMap[vmFirstFreePage], i = vmFirstFreePage;
  2530.          i < vmStat.numPhysPages;
  2531.      i++, corePtr++) {
  2532.     if ((corePtr->flags & VM_FREE_PAGE) || corePtr->lockCount > 0) {
  2533.         continue;
  2534.     }
  2535.     if (corePtr->flags & VM_DIRTY_PAGE) {
  2536.         numDirtyPages++;
  2537.         continue;
  2538.     }
  2539.     ptePtr = VmGetAddrPTEPtr(&corePtr->virtPage, corePtr->virtPage.page);
  2540.     if (*ptePtr & VM_MODIFIED_BIT) {
  2541.         numDirtyPages++;
  2542.         continue;
  2543.     }
  2544.     VmMach_GetRefModBits(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr),
  2545.                  &referenced, &modified);
  2546.     if (modified) {
  2547.         numDirtyPages++;
  2548.     }
  2549.     }
  2550.     UNLOCK_MONITOR;
  2551.     return(numDirtyPages);
  2552. }
  2553.  
  2554. /*
  2555.  * ----------------------------------------------------------------------------
  2556.  *
  2557.  * VmFlushSegment --
  2558.  *
  2559.  *    Flush the given range of pages in the segment to swap space and take
  2560.  *    them out of memory.  It is assumed that the processes that own this
  2561.  *    segment are frozen.
  2562.  *
  2563.  * Results:
  2564.  *         None.
  2565.  *
  2566.  * Side effects:
  2567.  *         All memory in the given range is forced out to swap and freed.
  2568.  *    *virtAddrPtr is modified.
  2569.  *
  2570.  * ----------------------------------------------------------------------------
  2571.  */
  2572. ENTRY void
  2573. VmFlushSegment(virtAddrPtr, lastPage)
  2574.     Vm_VirtAddr    *virtAddrPtr;
  2575.     int        lastPage;
  2576. {
  2577.     register    Vm_PTE        *ptePtr;
  2578.     register    VmCore        *corePtr;
  2579.     unsigned int        pfNum;
  2580.     Boolean            referenced;
  2581.     Boolean            modified;
  2582.  
  2583.     LOCK_MONITOR;
  2584.  
  2585.     if (virtAddrPtr->segPtr->ptPtr == (Vm_PTE *)NIL) {
  2586.     UNLOCK_MONITOR;
  2587.     return;
  2588.     }
  2589.     for (ptePtr = VmGetAddrPTEPtr(virtAddrPtr, virtAddrPtr->page);
  2590.          virtAddrPtr->page <= lastPage;
  2591.      virtAddrPtr->page++, VmIncPTEPtr(ptePtr, 1)) {
  2592.     if (!(*ptePtr & VM_PHYS_RES_BIT)) {
  2593.         continue;
  2594.     }
  2595.     pfNum = Vm_GetPageFrame(*ptePtr);
  2596.     corePtr = &coreMap[pfNum];
  2597.     if (corePtr->lockCount > 0) {
  2598.         continue;
  2599.     }
  2600.     if (corePtr->flags & VM_DIRTY_PAGE) {
  2601.         corePtr->flags |= VM_DONT_FREE_UNTIL_CLEAN;
  2602.     } else {
  2603.         VmMach_GetRefModBits(virtAddrPtr, Vm_GetPageFrame(*ptePtr),
  2604.                  &referenced, &modified);
  2605.         if ((*ptePtr & VM_MODIFIED_BIT) || modified) {
  2606.         TakeOffAllocList(corePtr);
  2607.         PutOnDirtyList(corePtr);
  2608.         corePtr->flags |= VM_DONT_FREE_UNTIL_CLEAN;
  2609.         }
  2610.     }
  2611.     VmPageFreeInt(pfNum);
  2612.     VmPageInvalidateInt(virtAddrPtr, ptePtr);
  2613.     }
  2614.  
  2615.     UNLOCK_MONITOR;
  2616. }
  2617.  
  2618.  
  2619. /*
  2620.  *----------------------------------------------------------------------
  2621.  *
  2622.  * Vm_GetPageSize --
  2623.  *
  2624.  *      Return the page size.
  2625.  *
  2626.  * Results:
  2627.  *      The page size.
  2628.  *
  2629.  * Side effects:
  2630.  *      None.
  2631.  *
  2632.  *----------------------------------------------------------------------
  2633.  */
  2634. int
  2635. Vm_GetPageSize()
  2636. {
  2637.     return(vm_PageSize);
  2638. }
  2639.  
  2640.  
  2641. /*
  2642.  *----------------------------------------------------------------------
  2643.  *
  2644.  * Vm_MapBlock --
  2645.  *
  2646.  *      Allocate and validate enough pages at the given address to map
  2647.  *    one FS cache block.
  2648.  *
  2649.  * Results:
  2650.  *      The number of pages that were allocated.
  2651.  *
  2652.  * Side effects:
  2653.  *      Pages added to kernels address space.
  2654.  *
  2655.  *----------------------------------------------------------------------
  2656.  */
  2657. int
  2658. Vm_MapBlock(addr)
  2659.     Address    addr;    /* Address where to map in pages. */
  2660. {
  2661.     register    Vm_PTE    *ptePtr;
  2662.     Vm_VirtAddr        virtAddr;
  2663.     unsigned    int    page;
  2664.     int            curFSPages;
  2665.  
  2666.     vmStat.fsMap++;
  2667.     curFSPages = vmStat.fsMap - vmStat.fsUnmap;
  2668.     if (curFSPages >= vmBoundary) {
  2669.     vmBoundary += vmPagesPerGroup;
  2670.     vmCurPenalty += vmFSPenalty;
  2671.     }
  2672.     if (curFSPages > vmStat.maxFSPages) {
  2673.     vmStat.maxFSPages = curFSPages;
  2674.     }
  2675.  
  2676.     virtAddr.page = (unsigned int) addr >> vmPageShift;
  2677.     virtAddr.offset = 0;
  2678.     virtAddr.segPtr = vm_SysSegPtr;
  2679.     virtAddr.flags = 0;
  2680.     virtAddr.sharedPtr = (Vm_SegProcList *)NIL;
  2681.     ptePtr = VmGetPTEPtr(vm_SysSegPtr, virtAddr.page);
  2682.  
  2683.     /*
  2684.      * Allocate a block.  We know that the page size is not smaller than
  2685.      * the block size so that one page will suffice.
  2686.      */
  2687.     page = DoPageAllocate(&virtAddr, 0);
  2688.     if (page == VM_NO_MEM_VAL) {
  2689.     /*
  2690.      * Couldn't get any memory.  
  2691.      */
  2692.     return(0);
  2693.     }
  2694.     *ptePtr |= page;
  2695.     VmPageValidate(&virtAddr);
  2696.  
  2697.     return(1);
  2698. }
  2699.  
  2700.  
  2701. /*
  2702.  *----------------------------------------------------------------------
  2703.  *
  2704.  * Vm_UnmapBlock --
  2705.  *
  2706.  *      Free and invalidate enough pages at the given address to unmap
  2707.  *    one fs cache block.
  2708.  *
  2709.  * Results:
  2710.  *      The number of pages that were deallocated.
  2711.  *
  2712.  * Side effects:
  2713.  *      Pages removed from kernels address space.
  2714.  *
  2715.  *----------------------------------------------------------------------
  2716.  */
  2717. int
  2718. Vm_UnmapBlock(addr, retOnePage, pageNumPtr)
  2719.     Address    addr;        /* Address where to map in pages. */
  2720.     Boolean    retOnePage;    /* TRUE => don't put one of the pages on
  2721.                  * the free list and return its value in
  2722.                  * *pageNumPtr. */
  2723.     unsigned int *pageNumPtr;    /* One of the pages that was unmapped. */
  2724. {
  2725.     register    Vm_PTE    *ptePtr;
  2726.     Vm_VirtAddr        virtAddr;
  2727.     int            curFSPages;
  2728.  
  2729.     vmStat.fsUnmap++;
  2730.     curFSPages = vmStat.fsMap - vmStat.fsUnmap;
  2731.  
  2732.     if (curFSPages < vmBoundary) {
  2733.     vmBoundary -= vmPagesPerGroup;
  2734.     vmCurPenalty -= vmFSPenalty;
  2735.     }
  2736.     if (curFSPages < vmStat.minFSPages) {
  2737.     vmStat.minFSPages = curFSPages;
  2738.     }
  2739.  
  2740.     virtAddr.page = (unsigned int) addr >> vmPageShift;
  2741.     virtAddr.offset = 0;
  2742.     virtAddr.segPtr = vm_SysSegPtr;
  2743.     virtAddr.flags = 0;
  2744.     virtAddr.sharedPtr = (Vm_SegProcList *)NIL;
  2745.     ptePtr = VmGetPTEPtr(vm_SysSegPtr, virtAddr.page);
  2746.  
  2747.     if (retOnePage) {
  2748.     *pageNumPtr = Vm_GetPageFrame(*ptePtr);
  2749.     } else {
  2750.     /*
  2751.      * If we aren't supposed to return the page, then free it.
  2752.      */
  2753.     VmPageFree(Vm_GetPageFrame(*ptePtr));
  2754.     }
  2755.     VmPageInvalidate(&virtAddr);
  2756.  
  2757.     return(1);
  2758. }
  2759.  
  2760.  
  2761. /*
  2762.  *----------------------------------------------------------------------
  2763.  *
  2764.  * Vm_FsCacheSize --
  2765.  *
  2766.  *    Return the virtual addresses of the start and end of the file systems
  2767.  *    cache.
  2768.  *
  2769.  * Results:
  2770.  *    None.
  2771.  *
  2772.  * Side effects:
  2773.  *    None.
  2774.  *
  2775.  *----------------------------------------------------------------------
  2776.  */
  2777. void
  2778. Vm_FsCacheSize(startAddrPtr, endAddrPtr)
  2779.     Address    *startAddrPtr;    /* Lowest virtual address. */
  2780.     Address    *endAddrPtr;    /* Highest virtual address. */
  2781. {
  2782.     int    numPages;
  2783.  
  2784.     /*
  2785.      * Compute the minimum number of pages that are reserved for VM.  The number
  2786.      * of free pages is the maximum number of pages that will ever exist
  2787.      * for user processes.
  2788.      */
  2789.     vmStat.minVMPages = vmStat.numFreePages / MIN_VM_PAGE_FRACTION;
  2790.     vmMaxDirtyPages = vmStat.numFreePages / MAX_DIRTY_PAGE_FRACTION;
  2791.  
  2792.     *startAddrPtr = vmBlockCacheBaseAddr;
  2793.     /*
  2794.      * We aren't going to get any more free pages so limit the maximum number
  2795.      * of blocks in the cache to the number of free pages that we have minus
  2796.      * the minimum amount of free pages that we keep for user
  2797.      * processes to run.
  2798.      */
  2799.     numPages = ((unsigned int)vmBlockCacheEndAddr - 
  2800.         (unsigned int)vmBlockCacheBaseAddr) / vm_PageSize;
  2801.     if (numPages > vmStat.numFreePages - vmStat.minVMPages) {
  2802.     numPages = vmStat.numFreePages - vmStat.minVMPages;
  2803.     }
  2804.     *endAddrPtr = vmBlockCacheBaseAddr + numPages * vm_PageSize - 1;
  2805.     /*
  2806.      * Compute the penalties to put onto FS pages.
  2807.      */
  2808.     vmPagesPerGroup = vmStat.numFreePages / vmNumPageGroups;
  2809.     vmCurPenalty = 0;
  2810.     vmBoundary = vmPagesPerGroup;
  2811. }
  2812.  
  2813. /*---------------------------------------------------------------------------
  2814.  * 
  2815.  *    Routines for recovery
  2816.  *
  2817.  * VM needs to be able to recover when the server of swap crashes.  This is
  2818.  * done in the following manner:
  2819.  *
  2820.  *    1) At boot time the directory "/swap/host_number" is open by the
  2821.  *     routine Vm_OpenSwapDirectory and the stream is stored in 
  2822.  *     vmSwapStreamPtr.
  2823.  *    2) If an error occurs on a page write then the variable swapDown
  2824.  *     is set to TRUE which prohibits all further actions that would dirty
  2825.  *     physical memory pages (e.g. page faults) and prohibits dirty pages
  2826.  *     from being written to swap.
  2827.  *    3) Next the routine Fsutil_WaitForHost is called to asynchronously wait
  2828.  *     for the server to come up.  When it detects that the server is
  2829.  *     in fact up and the file system is alive, it calls Vm_Recovery.
  2830.  *    4) Vm_Recovery when called will set swapDown to FALSE and start cleaning
  2831.  *     dirty pages if necessary.
  2832.  */
  2833.  
  2834.  
  2835. /*
  2836.  *----------------------------------------------------------------------
  2837.  *
  2838.  * Vm_Recovery --
  2839.  *
  2840.  *    The swap area has just come back up.  Wake up anyone waiting for it to
  2841.  *    come back and start up page cleaners if there are dirty pages to be
  2842.  *    written out.
  2843.  *
  2844.  * Results:
  2845.  *    None.
  2846.  *
  2847.  * Side effects:
  2848.  *    swapDown flag set to FALSE.
  2849.  *
  2850.  *----------------------------------------------------------------------
  2851.  */
  2852. ENTRY void
  2853. Vm_Recovery()
  2854. {
  2855.     LOCK_MONITOR;
  2856.  
  2857.     swapDown = FALSE;
  2858.     Sync_Broadcast(&swapDownCondition);
  2859.     while (vmStat.numDirtyPages - numPageOutProcs > 0 &&
  2860.        numPageOutProcs < vmMaxPageOutProcs) { 
  2861.     Proc_CallFunc(PageOut, (ClientData) numPageOutProcs, 0);
  2862.     numPageOutProcs++;
  2863.     }
  2864.  
  2865.     UNLOCK_MONITOR;
  2866. }
  2867.  
  2868. /*
  2869.  *----------------------------------------------------------------------
  2870.  *
  2871.  * VmPageFlush --
  2872.  *
  2873.  *    Flush (shared) pages to the server or disk.
  2874.  *
  2875.  * Results:
  2876.  *    SUCCESS if it worked.
  2877.  *
  2878.  * Side effects:
  2879.  *    Page is written to disk and removed from memory.
  2880.  *    If page is pinned down, it will be unpinned.
  2881.  *    The page is invalidated from the local cache.
  2882.  *    *virtAddrPtr is modified.
  2883.  *
  2884.  *----------------------------------------------------------------------
  2885.  */
  2886. ENTRY ReturnStatus
  2887. VmPageFlush(virtAddrPtr, length, toDisk, wantRes)
  2888.     Vm_VirtAddr        *virtAddrPtr;
  2889.     int            length;
  2890.     Boolean        toDisk;
  2891.     Boolean        wantRes;
  2892. {
  2893.     VmCore        *corePtr;
  2894.     Fscache_FileInfo    *cacheInfoPtr;
  2895.     ReturnStatus    status = SUCCESS;
  2896.     ReturnStatus    statusTmp;
  2897.     int            firstBlock;
  2898.     Vm_Segment        *segPtr;
  2899.     Fs_Stream        *streamPtr;
  2900.     Vm_PTE        *ptePtr;
  2901.     int            lastPage;
  2902.     int            pageFrame;
  2903.     int            referenced, modified;
  2904.  
  2905.     LOCK_MONITOR;
  2906.     dprintf("VmPageFlush(%x, %d, %d, %d)\n", virtAddrPtr, length, toDisk,
  2907.         wantRes);
  2908.     segPtr = virtAddrPtr->segPtr;
  2909.     lastPage = virtAddrPtr->page + (length>>vmPageShift) - 1;
  2910.     streamPtr = segPtr->swapFilePtr;
  2911.     dprintf("segPtr = %x, firstPage = %d, lastPage = %d, streamPtr = %x\n",
  2912.        segPtr, virtAddrPtr->page, lastPage, streamPtr);
  2913.     for (ptePtr = VmGetAddrPTEPtr(virtAddrPtr, virtAddrPtr->page);
  2914.         virtAddrPtr->page <= lastPage;
  2915.         VmIncPTEPtr(ptePtr,1), virtAddrPtr->page++) {
  2916.     if (!(*ptePtr & VM_PHYS_RES_BIT)) {
  2917.         if (wantRes) {
  2918.         dprintf("Page is not physically resident\n");
  2919.         status = FAILURE;
  2920.         }
  2921.         continue;
  2922.     }
  2923.     pageFrame = Vm_GetPageFrame(*ptePtr);
  2924.     corePtr = &coreMap[pageFrame];
  2925.     referenced = *ptePtr & VM_REFERENCED_BIT;
  2926.     modified = *ptePtr & VM_MODIFIED_BIT;
  2927.     VmMach_AllocCheck(&corePtr->virtPage, pageFrame,
  2928.               &referenced, &modified);
  2929.     if (!modified) {
  2930.         dprintf("Page is clean, so skipping\n");
  2931.         continue;
  2932.     }
  2933.     *ptePtr |= VM_ON_SWAP_BIT;
  2934.     corePtr->flags |= VM_PAGE_BEING_CLEANED;
  2935.     *ptePtr &= ~VM_MODIFIED_BIT;
  2936.     dprintf("VmPageFlush: paging out %d (%d)\n", virtAddrPtr->page, 
  2937.         corePtr-coreMap);
  2938.     VmMach_ClearModBit(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr)); 
  2939.     UNLOCK_MONITOR;
  2940.     statusTmp = VmPageServerWrite(&corePtr->virtPage,
  2941.         (unsigned int)(corePtr-coreMap), toDisk);
  2942.     dprintf("VmPageFlush: status = %x, wrote %x, %x\n", statusTmp,
  2943.         &corePtr->virtPage, (unsigned int)(corePtr-coreMap));
  2944.     LOCK_MONITOR;
  2945.     corePtr->flags &= ~VM_PAGE_BEING_CLEANED;
  2946.     if (statusTmp != SUCCESS) {
  2947.         status = statusTmp;
  2948.         break;
  2949.     }
  2950.     /*
  2951.      * This stuff should probably be in the fs module.
  2952.      */
  2953.     if (streamPtr != (Fs_Stream *)NIL &&
  2954.         streamPtr->ioHandlePtr->fileID.type == FSIO_RMT_FILE_STREAM) {
  2955.         cacheInfoPtr = & ((Fsrmt_FileIOHandle *)streamPtr
  2956.             ->ioHandlePtr)->cacheInfo;
  2957.         if (segPtr->type == VM_STACK) {
  2958.         firstBlock = mach_LastUserStackPage - virtAddrPtr->page;
  2959.         } else if (segPtr->type == VM_SHARED) {
  2960.         firstBlock= virtAddrPtr->page - segOffset(virtAddrPtr) +
  2961.             (virtAddrPtr->sharedPtr->fileAddr>>vmPageShift);
  2962.         } else {
  2963.         firstBlock = virtAddrPtr->page - segPtr->offset;
  2964.         }
  2965.         dprintf("Invalidating block %d\n", firstBlock);
  2966.         Fscache_FileInvalidate(cacheInfoPtr, firstBlock,
  2967.         firstBlock+ (length>>vmPageShift)-1);
  2968.     }
  2969.     }
  2970.     UNLOCK_MONITOR;
  2971.     if (status != SUCCESS) {
  2972.     dprintf("VmPageFlush: failure: %x\n", status);
  2973.     }
  2974.     return status;
  2975. }
  2976.